diff -pruN 3.3.1-3/.github/workflows/blivet-tests.yml 3.4.0-1/.github/workflows/blivet-tests.yml
--- 3.3.1-3/.github/workflows/blivet-tests.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/.github/workflows/blivet-tests.yml	2025-09-24 13:46:42.000000000 +0000
@@ -13,7 +13,7 @@ jobs:
       CI_CONTAINER: libblockdev-ci-blivet-tests
     steps:
       - name: Checkout libblockdev repository
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
 
       - name: Install podman
         run: |
diff -pruN 3.3.1-3/.github/workflows/codeql-analysis.yml 3.4.0-1/.github/workflows/codeql-analysis.yml
--- 3.3.1-3/.github/workflows/codeql-analysis.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/.github/workflows/codeql-analysis.yml	2025-09-24 13:46:42.000000000 +0000
@@ -24,7 +24,7 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
diff -pruN 3.3.1-3/.github/workflows/compilation.yml 3.4.0-1/.github/workflows/compilation.yml
--- 3.3.1-3/.github/workflows/compilation.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/.github/workflows/compilation.yml	2025-09-24 13:46:42.000000000 +0000
@@ -17,7 +17,7 @@ jobs:
                    'clang-14', 'clang-15', 'clang-16', 'clang-17', 'clang-18']
 
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
 
     - name: Install build dependencies
       run: |
diff -pruN 3.3.1-3/.github/workflows/csmock.yml 3.4.0-1/.github/workflows/csmock.yml
--- 3.3.1-3/.github/workflows/csmock.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/.github/workflows/csmock.yml	2025-09-24 13:46:42.000000000 +0000
@@ -17,7 +17,7 @@ jobs:
       CI_CONTAINER: libblockdev-ci-csmock
     steps:
       - name: Checkout libblockdev repository
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
 
       - name: Install podman
         run: |
diff -pruN 3.3.1-3/.github/workflows/spelling.yml 3.4.0-1/.github/workflows/spelling.yml
--- 3.3.1-3/.github/workflows/spelling.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/.github/workflows/spelling.yml	2025-09-24 13:46:42.000000000 +0000
@@ -13,7 +13,7 @@ jobs:
       CI_CONTAINER: libblockdev-ci-spelling
     steps:
       - name: Checkout libblockdev repository
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           path: libblockdev
 
@@ -23,7 +23,7 @@ jobs:
           sudo apt -y -qq install codespell lintian
 
       - name: Get the storaged-project/ci repository
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           repository: storaged-project/ci
           path: ci
diff -pruN 3.3.1-3/.github/workflows/udisks-build.yml 3.4.0-1/.github/workflows/udisks-build.yml
--- 3.3.1-3/.github/workflows/udisks-build.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/.github/workflows/udisks-build.yml	2025-09-24 13:46:42.000000000 +0000
@@ -13,7 +13,7 @@ jobs:
       CI_CONTAINER: libblockdev-ci-udisks-build
     steps:
       - name: Checkout libblockdev repository
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
 
       - name: Install podman
         run: |
diff -pruN 3.3.1-3/.packit.yaml 3.4.0-1/.packit.yaml
--- 3.3.1-3/.packit.yaml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/.packit.yaml	2025-09-24 13:46:42.000000000 +0000
@@ -13,18 +13,26 @@ actions:
 
 jobs:
 - job: copr_build
-  metadata:
-    targets:
-    - fedora-rawhide-aarch64
-    - fedora-rawhide-ppc64le
-    - fedora-rawhide-x86_64
-    - fedora-latest-aarch64
-    - fedora-latest-ppc64le
-    - fedora-latest-x86_64
-    - fedora-latest-stable-aarch64
-    - fedora-latest-stable-ppc64le
-    - fedora-latest-stable-x86_64
+  targets:
+  - fedora-rawhide-aarch64
+  - fedora-rawhide-ppc64le
+  - fedora-rawhide-x86_64
+  - fedora-latest-aarch64
+  - fedora-latest-ppc64le
+  - fedora-latest-x86_64
+  - fedora-latest-stable-aarch64
+  - fedora-latest-stable-ppc64le
+  - fedora-latest-stable-x86_64
   trigger: pull_request
+  branch: master
+
+- job: copr_build
+  targets:
+    - centos-stream-10-aarch64
+    - centos-stream-10-ppc64le
+    - centos-stream-10-x86_64
+  trigger: pull_request
+  branch: rhel10-branch
 
 - job: copr_build
   trigger: commit
@@ -88,7 +96,17 @@ jobs:
 - job: tests
   trigger: pull_request
   targets:
-    - fedora-latest-stable
+    - fedora-latest-x86_64
+    - fedora-latest-aarch64
+    - fedora-latest-stable-x86_64
+    - fedora-latest-stable-aarch64
+  branch: master
+
+- job: tests
+  trigger: pull_request
+  targets:
+    - centos-stream-10-x86_64
+  branch: rhel10-branch
 
 # run tests for libblockdev consumers, see plans/ with `revdeps_blivet == yes`
 - job: tests
@@ -99,6 +117,7 @@ jobs:
       message: "Blivet tests failed for commit {commit_sha}. @vojtechtrefny please check."
   targets:
     - fedora-latest-stable
+  branch: master
   tf_extra_params:
     environments:
       - artifacts:
@@ -116,6 +135,7 @@ jobs:
       message: "udisks tests failed for commit {commit_sha}. @vojtechtrefny @tbzatek please check."
   targets:
     - fedora-latest-stable
+  branch: master
   tf_extra_params:
     environments:
       - artifacts:
diff -pruN 3.3.1-3/Makefile.am 3.4.0-1/Makefile.am
--- 3.3.1-3/Makefile.am	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/Makefile.am	2025-09-24 13:46:42.000000000 +0000
@@ -76,7 +76,7 @@ MAINTAINERCLEANFILES = Makefile.in acloc
     configure depcomp install-sh ltmain.sh missing py-compile compile ar-lib \
     m4/*.m4
 
-LIBDIRS = src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/plugins/nvme/.libs:src/plugins/smart/.libs:src/lib/.libs
+LIBDIRS = src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/plugins/lvm/.libs:src/plugins/nvme/.libs:src/plugins/smart/.libs:src/lib/.libs
 GIDIR = src/lib
 
 if WITH_PYTHON3
diff -pruN 3.3.1-3/NEWS.rst 3.4.0-1/NEWS.rst
--- 3.3.1-3/NEWS.rst	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/NEWS.rst	2025-09-24 13:46:42.000000000 +0000
@@ -1,3 +1,79 @@
+Libblockdev 3.4.0
+
+New minor release of the libblockdev library with multiple fixes and new features. See below
+for details.
+
+**Notable changes**
+
+- bd_nvme_connect() now defaults to port 4420 or 8009 for discovery NQN respectively when
+the transport_svcid argument is not specified.
+
+**Full list of changes**
+
+Rodrigo Cox (1):
+
+- tests: Simplify NVMe controller waiting with basic polling
+
+Sam James (1):
+
+- configure.ac: fix bashism
+
+Tomas Bzatek (4):
+
+- smart: Use drive self-assessment as an overall status
+- tests: Fix NVMe sanitize log return codes
+- nvme: Default to well-known tr_svcid values when not specified
+- nvme: Handle memory allocation failures from _nvme_alloc()
+
+Vojtech Trefny (41):
+
+- tests: Skip LVM VDO tests if 'vdoformat' is not available
+- crypto: Add a function to set persistent flags for LUKS
+- docs: Add missing functions to docs/libblockdev-sections.txt
+- tests: Skip NVDIMM reconfigure tests on rawhide
+- ci: Switch branch for Blivet revdeps tests to 'main'
+- tests: Skip NVDIMM tests completely
+- Move LVM plugin to a separate directory
+- Move common LVM code to a separate file
+- Adjust library tests to the new LVM plugin directory structure
+- tests: Fix printing skipped test cases without docstring
+- lvm: Add support for running vgcfgbackup/restore
+- New version - 3.3.1
+- tests: Temporarily skip CryptoTestOpenClose with LUKS1 on rawhide
+- packit: Limit Fedora builds and tests to the 'master' branch
+- packit: Add RPM build for pull requests against the rhel10-branch
+- packit: Add tmt tests for rhel10-branch running on C10S
+- packit: Remove obsolete metadata dictionary from copr_build job
+- tests: Skip MDTestNominateDenominate in RHEL/CentOS 10
+- Fix parsing subvolumes with spaces in name
+- tests: Skip MDTestNominateDenominate on RHEL/CentOS 9 too
+- tests: Fix skipping escrow tests in FIPS mode
+- tests: Remove code duplication in LVM and LVM DBus tests
+- tests: Create more devices only for tests that need more devices
+- tests: Merge some of the LUKS test cases
+- crypto: Allow setting PBKDF arguments for LUKS1 too
+- tests: Use fast PBKDF parameters when creating LUKS devices
+- tests: Do not create two disks for all FS tests
+- tests: Do not require extra disks for spares for all MD tests
+- tests: Merge MD (de)activation test cases into one
+- tests: Use RAID0 for MD (de)activation test case
+- tests: Do not create two disks for all part tests
+- tests: Create a proper single "NoDev" test case for part tests
+- tests: Fix issues found by pylint
+- tests: Merge swap label and UUID test cases
+- misc: Do not try to install VDO on 32bit Debian
+- tests: Fix SCSI debug disks setup and cleanup in SMART tests
+- ci: Enable tests on aarch64 with packit and tmt
+- lvm-dbus: Fix calling lvcreate with empty list of PVs
+- Add a function to re-read size of the loop device
+- Fix issues discovered by static analysis
+- More static analysis issues fixes
+
+dependabot[bot] (1):
+
+- infra: bump actions/checkout from 4 to 5
+
+
 Libblockdev 3.3.1
 ------------------
 
diff -pruN 3.3.1-3/configure.ac 3.4.0-1/configure.ac
--- 3.3.1-3/configure.ac	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/configure.ac	2025-09-24 13:46:42.000000000 +0000
@@ -1,6 +1,6 @@
 # configure.ac for libblockdev
 
-AC_INIT([libblockdev], [3.3.1], [], [], [https://github.com/storaged-project/libblockdev])
+AC_INIT([libblockdev], [3.4.0], [], [], [https://github.com/storaged-project/libblockdev])
 
 # Disable building static libraries.
 # This needs to be set before initializing automake
@@ -21,6 +21,7 @@ LT_INIT
 AC_CONFIG_FILES([Makefile src/Makefile \
                           src/plugins/Makefile \
                           src/plugins/fs/Makefile \
+                          src/plugins/lvm/Makefile \
                           src/plugins/nvme/Makefile \
                           src/plugins/smart/Makefile \
                           src/utils/Makefile \
@@ -207,6 +208,8 @@ AS_IF([test "x$with_crypto" != "xno"],
             [AC_DEFINE([LIBCRYPTSETUP_26])], [])
       AS_IF([$PKG_CONFIG --atleast-version=2.7.0 libcryptsetup],
             [AC_DEFINE([LIBCRYPTSETUP_27])], [])
+      AS_IF([$PKG_CONFIG --atleast-version=2.8.0 libcryptsetup],
+            [AC_DEFINE([LIBCRYPTSETUP_28])], [])
       AC_CHECK_HEADER([linux/sed-opal.h],
                       [AC_DEFINE([HAVE_LINUX_OPAL])], [])
       AS_IF([test "x$with_escrow" != "xno"],
@@ -310,7 +313,7 @@ AS_IF([test "x$with_smart" != "xno"],
 
 AS_IF([test "x$with_smart" != "xno"],
       [SAVED_CFLAGS=$CFLAGS
-       CFLAGS+=" -I$drivedb_path"
+       CFLAGS="${CFLAGS} -I$drivedb_path"
        AC_MSG_CHECKING([for drivedb.h presence])
        AC_COMPILE_IFELSE(
            [AC_LANG_PROGRAM([
diff -pruN 3.3.1-3/debian/changelog 3.4.0-1/debian/changelog
--- 3.3.1-3/debian/changelog	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/changelog	2025-10-14 13:36:07.000000000 +0000
@@ -1,3 +1,13 @@
+libblockdev (3.4.0-1) unstable; urgency=medium
+
+  * New upstream release
+  * Rebase patches
+  * Update symbols files
+  * Mark _get_linux_version() as static
+  * Update debian/copyright
+
+ -- Michael Biebl <biebl@debian.org>  Tue, 14 Oct 2025 15:36:07 +0200
+
 libblockdev (3.3.1-3) unstable; urgency=medium
 
   * Switch debian-branch to debian/latest as per DEP-14
diff -pruN 3.3.1-3/debian/copyright 3.4.0-1/debian/copyright
--- 3.3.1-3/debian/copyright	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/copyright	2025-10-14 13:36:07.000000000 +0000
@@ -3,7 +3,7 @@ Upstream-Name: libblockdev
 Source: https://github.com/storaged-project/libblockdev
 
 Files: *
-Copyright: 2014-2021 Red Hat, Inc.
+Copyright: 2014-2024 Red Hat, Inc.
 License: LGPL-2.1+
 
 Files: debian/*
@@ -12,7 +12,7 @@ Copyright: 2016 Peter Hatina <phatina@re
 License: GPL-2+
 
 License: LGPL-2.1+
- This file is free software; you can redistribute it and/or
+ This package is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
@@ -22,10 +22,9 @@ License: LGPL-2.1+
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  .
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- .
+ You should have received a copy of the GNU Lesser General Public License
+ along with this package. If not, see <https://www.gnu.org/licenses/>.
+Comment:
  On Debian systems, the complete text of the GNU Lesser General Public
  License version 2.1 can be found in "/usr/share/common-licenses/LGPL-2.1".
 
@@ -42,6 +41,6 @@ License: GPL-2+
  .
  You should have received a copy of the GNU General Public License
  along with this program. If not, see <https://www.gnu.org/licenses/>
- .
- On Debian systems, the complete text of the GNU General
- Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
+Comment:
+ On Debian systems, the complete text of the GNU General Public
+ License version 2 can be found in "/usr/share/common-licenses/GPL-2".
diff -pruN 3.3.1-3/debian/libblockdev-crypto3.symbols 3.4.0-1/debian/libblockdev-crypto3.symbols
--- 3.3.1-3/debian/libblockdev-crypto3.symbols	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/libblockdev-crypto3.symbols	2025-10-14 13:36:07.000000000 +0000
@@ -53,6 +53,7 @@ libbd_crypto.so.3 libblockdev-crypto3 #M
  bd_crypto_luks_resize@Base 2.14
  bd_crypto_luks_resume@Base 2.20
  bd_crypto_luks_set_label@Base 3.0
+ bd_crypto_luks_set_persistent_flags@Base 3.4.0
  bd_crypto_luks_set_uuid@Base 3.0
  bd_crypto_luks_status@Base 2.14
  bd_crypto_luks_suspend@Base 2.20
diff -pruN 3.3.1-3/debian/libblockdev-loop3.symbols 3.4.0-1/debian/libblockdev-loop3.symbols
--- 3.3.1-3/debian/libblockdev-loop3.symbols	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/libblockdev-loop3.symbols	2025-10-14 13:36:07.000000000 +0000
@@ -9,6 +9,7 @@ libbd_loop.so.3 libblockdev-loop3 #MINVE
  bd_loop_init@Base 2.14
  bd_loop_is_tech_avail@Base 2.14
  bd_loop_set_autoclear@Base 2.14
+ bd_loop_set_capacity@Base 3.4.0
  bd_loop_setup@Base 2.14
  bd_loop_setup_from_fd@Base 2.14
  bd_loop_teardown@Base 2.14
diff -pruN 3.3.1-3/debian/libblockdev-lvm-dbus3.symbols 3.4.0-1/debian/libblockdev-lvm-dbus3.symbols
--- 3.3.1-3/debian/libblockdev-lvm-dbus3.symbols	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/libblockdev-lvm-dbus3.symbols	2025-10-14 13:36:07.000000000 +0000
@@ -86,6 +86,8 @@ libbd_lvm-dbus.so.3 libblockdev-lvm-dbus
  bd_lvm_vdo_resize@Base 2.24
  bd_lvm_vdolvpoolname@Base 2.24
  bd_lvm_vgactivate@Base 2.14
+ bd_lvm_vgcfgbackup@Base 3.4.0
+ bd_lvm_vgcfgrestore@Base 3.4.0
  bd_lvm_vgcreate@Base 2.14
  bd_lvm_vgdata_copy@Base 2.14
  bd_lvm_vgdata_free@Base 2.14
diff -pruN 3.3.1-3/debian/libblockdev-lvm3.symbols 3.4.0-1/debian/libblockdev-lvm3.symbols
--- 3.3.1-3/debian/libblockdev-lvm3.symbols	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/libblockdev-lvm3.symbols	2025-10-14 13:36:07.000000000 +0000
@@ -90,6 +90,8 @@ libbd_lvm.so.3 libblockdev-lvm3 #MINVER#
  bd_lvm_vdopooldata_copy@Base 2.25
  bd_lvm_vdopooldata_free@Base 2.25
  bd_lvm_vgactivate@Base 2.14
+ bd_lvm_vgcfgbackup@Base 3.4.0
+ bd_lvm_vgcfgrestore@Base 3.4.0
  bd_lvm_vgcreate@Base 2.14
  bd_lvm_vgdata_copy@Base 2.14
  bd_lvm_vgdata_free@Base 2.14
diff -pruN 3.3.1-3/debian/libblockdev3.symbols 3.4.0-1/debian/libblockdev3.symbols
--- 3.3.1-3/debian/libblockdev3.symbols	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/libblockdev3.symbols	2025-10-14 13:36:07.000000000 +0000
@@ -85,6 +85,7 @@ libblockdev.so.3 libblockdev3 #MINVER#
  bd_crypto_luks_resize@Base 2.14
  bd_crypto_luks_resume@Base 2.20
  bd_crypto_luks_set_label@Base 3.0
+ bd_crypto_luks_set_persistent_flags@Base 3.4.0
  bd_crypto_luks_set_uuid@Base 3.0
  bd_crypto_luks_status@Base 2.14
  bd_crypto_luks_suspend@Base 2.20
@@ -289,6 +290,7 @@ libblockdev.so.3 libblockdev3 #MINVER#
  bd_loop_info_get_type@Base 3.0
  bd_loop_is_tech_avail@Base 2.14
  bd_loop_set_autoclear@Base 2.14
+ bd_loop_set_capacity@Base 3.4.0
  bd_loop_setup@Base 2.14
  bd_loop_setup_from_fd@Base 2.14
  bd_loop_teardown@Base 2.14
@@ -388,6 +390,8 @@ libblockdev.so.3 libblockdev3 #MINVER#
  bd_lvm_vdopooldata_free@Base 2.25
  bd_lvm_vdopooldata_get_type@Base 2.25
  bd_lvm_vgactivate@Base 2.14
+ bd_lvm_vgcfgbackup@Base 3.4.0
+ bd_lvm_vgcfgrestore@Base 3.4.0
  bd_lvm_vgcreate@Base 2.14
  bd_lvm_vgdata_copy@Base 2.14
  bd_lvm_vgdata_free@Base 2.14
diff -pruN 3.3.1-3/debian/patches/Mark-_get_linux_version-as-static.patch 3.4.0-1/debian/patches/Mark-_get_linux_version-as-static.patch
--- 3.3.1-3/debian/patches/Mark-_get_linux_version-as-static.patch	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/debian/patches/Mark-_get_linux_version-as-static.patch	2025-10-14 13:36:07.000000000 +0000
@@ -0,0 +1,24 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Tue, 14 Oct 2025 14:30:12 +0200
+Subject: Mark _get_linux_version() as static
+
+It is an internal implementation detail and the symbol should not be
+exported via libblockdev-utils.
+Forwarded: https://github.com/storaged-project/libblockdev/pull/1135
+---
+ src/utils/module.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/utils/module.c b/src/utils/module.c
+index 6e41796..8f2b772 100644
+--- a/src/utils/module.c
++++ b/src/utils/module.c
+@@ -274,7 +274,7 @@ static gboolean have_linux_ver = FALSE;
+ 
+ G_LOCK_DEFINE_STATIC (detected_linux_ver);
+ 
+-BDUtilsLinuxVersion * _get_linux_version (gboolean lock, GError **error) {
++static BDUtilsLinuxVersion * _get_linux_version (gboolean lock, GError **error) {
+     struct utsname buf;
+ 
+     if (lock)
diff -pruN 3.3.1-3/debian/patches/Skip-smartmontools-integration-test.patch 3.4.0-1/debian/patches/Skip-smartmontools-integration-test.patch
--- 3.3.1-3/debian/patches/Skip-smartmontools-integration-test.patch	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/patches/Skip-smartmontools-integration-test.patch	2025-10-14 13:36:07.000000000 +0000
@@ -20,7 +20,7 @@ gi.repository.GLib.GError: g-bd-init-err
  1 file changed, 5 insertions(+)
 
 diff --git a/tests/skip.yml b/tests/skip.yml
-index 2a522d0..2564399 100644
+index d565e31..f3d4c87 100644
 --- a/tests/skip.yml
 +++ b/tests/skip.yml
 @@ -24,6 +24,11 @@
diff -pruN 3.3.1-3/debian/patches/series 3.4.0-1/debian/patches/series
--- 3.3.1-3/debian/patches/series	2025-08-15 12:47:16.000000000 +0000
+++ 3.4.0-1/debian/patches/series	2025-10-14 13:36:07.000000000 +0000
@@ -1 +1,2 @@
 Skip-smartmontools-integration-test.patch
+Mark-_get_linux_version-as-static.patch
diff -pruN 3.3.1-3/dist/libblockdev.spec.in 3.4.0-1/dist/libblockdev.spec.in
--- 3.3.1-3/dist/libblockdev.spec.in	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/dist/libblockdev.spec.in	2025-09-24 13:46:42.000000000 +0000
@@ -85,7 +85,7 @@
 %define configure_opts %{?python3_copts} %{?lvm_dbus_copts} %{?btrfs_copts} %{?crypto_copts} %{?dm_copts} %{?loop_copts} %{?lvm_copts} %{?lvm_dbus_copts} %{?mdraid_copts} %{?mpath_copts} %{?swap_copts} %{?part_copts} %{?fs_copts} %{?nvdimm_copts} %{?tools_copts} %{?gi_copts} %{?nvme_copts} %{?smart_copts} %{?smartmontools_copts}
 
 Name:        libblockdev
-Version:     3.3.1
+Version:     3.4.0
 Release:     1%{?dist}
 Summary:     A library for low-level manipulation with block devices
 License:     LGPL-2.1-or-later
@@ -958,6 +958,57 @@ find %{buildroot} -type f -name "*.la" |
 %files plugins-all
 
 %changelog
+* Wed Sep 24 2025 Vojtech Trefny <vtrefny@redhat.com> - 3.4.0-1
+- More static analysis issues fixes (vtrefny)
+- nvme: Handle memory allocation failures from _nvme_alloc() (tbzatek)
+- nvme: Default to well-known tr_svcid values when not specified (tbzatek)
+- Fix issues discovered by static analysis (vtrefny)
+- Add a function to re-read size of the loop device (vtrefny)
+- tests: Simplify NVMe controller waiting with basic polling (ermezavx)
+- lvm-dbus: Fix calling lvcreate with empty list of PVs (vtrefny)
+- ci: Enable tests on aarch64 with packit and tmt (vtrefny)
+- tests: Fix SCSI debug disks setup and cleanup in SMART tests (vtrefny)
+- misc: Do not try to install VDO on 32bit Debian (vtrefny)
+- infra: bump actions/checkout from 4 to 5 (49699333+dependabot[bot])
+- tests: Merge swap label and UUID test cases (vtrefny)
+- tests: Fix issues found by pylint (vtrefny)
+- tests: Create a proper single "NoDev" test case for part tests (vtrefny)
+- tests: Do not create two disks for all part tests (vtrefny)
+- tests: Use RAID0 for MD (de)activation test case (vtrefny)
+- tests: Merge MD (de)activation test cases into one (vtrefny)
+- tests: Do not require extra disks for spares for all MD tests (vtrefny)
+- tests: Do not create two disks for all FS tests (vtrefny)
+- tests: Use fast PBKDF parameters when creating LUKS devices (vtrefny)
+- crypto: Allow setting PBKDF arguments for LUKS1 too (vtrefny)
+- tests: Merge some of the LUKS test cases (vtrefny)
+- tests: Create more devices only for tests that need more devices (vtrefny)
+- tests: Remove code duplication in LVM and LVM DBus tests (vtrefny)
+- tests: Fix skipping escrow tests in FIPS mode (vtrefny)
+- tests: Skip MDTestNominateDenominate on RHEL/CentOS 9 too (vtrefny)
+- Fix parsing subvolumes with spaces in name (vtrefny)
+- tests: Skip MDTestNominateDenominate in RHEL/CentOS 10 (vtrefny)
+- packit: Remove obsolete metadata dictionary from copr_build job (vtrefny)
+- packit: Add tmt tests for rhel10-branch running on C10S (vtrefny)
+- packit: Add RPM build for pull requests against the rhel10-branch (vtrefny)
+- packit: Limit Fedora builds and tests to the 'master' branch (vtrefny)
+- tests: Temporarily skip CryptoTestOpenClose with LUKS1 on rawhide (vtrefny)
+- New version - 3.3.1 (vtrefny)
+- configure.ac: fix bashism (sam)
+- Don't allow suid and dev set on fs resize (Thomas.Blume)
+- lvm: Add support for running vgcfgbackup/restore (vtrefny)
+- tests: Fix printing skipped test cases without docstring (vtrefny)
+- Adjust library tests to the new LVM plugin directory structure (vtrefny)
+- Move common LVM code to a separate file (vtrefny)
+- Move LVM plugin to a separate directory (vtrefny)
+- tests: Skip NVDIMM tests completely (vtrefny)
+- tests: Fix NVMe sanitize log return codes (tbzatek)
+- ci: Switch branch for Blivet revdeps tests to 'main' (vtrefny)
+- tests: Skip NVDIMM reconfigure tests on rawhide (vtrefny)
+- smart: Use drive self-assessment as an overall status (tbzatek)
+- docs: Add missing functions to docs/libblockdev-sections.txt (vtrefny)
+- crypto: Add a function to set persistent flags for LUKS (vtrefny)
+- tests: Skip LVM VDO tests if 'vdoformat' is not available (vtrefny)
+
 * Wed Jun 18 2025 Vojtech Trefny <vtrefny@redhat.com> - 3.3.1-1
 - Don't allow suid and dev set on fs resize (Thomas.Blume)
 
diff -pruN 3.3.1-3/docs/libblockdev-sections.txt 3.4.0-1/docs/libblockdev-sections.txt
--- 3.3.1-3/docs/libblockdev-sections.txt	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/docs/libblockdev-sections.txt	2025-09-24 13:46:42.000000000 +0000
@@ -94,6 +94,8 @@ bd_crypto_luks_header_restore
 bd_crypto_luks_set_label
 bd_crypto_luks_set_uuid
 bd_crypto_luks_convert
+BDCryptoLUKSPersistentFlags
+bd_crypto_luks_set_persistent_flags
 BDCryptoLUKSInfo
 bd_crypto_luks_info_free
 bd_crypto_luks_info_copy
@@ -261,6 +263,7 @@ bd_loop_setup
 bd_loop_setup_from_fd
 bd_loop_teardown
 bd_loop_set_autoclear
+bd_loop_set_capacity
 BDLoopTech
 BDLoopTechMode
 bd_loop_is_tech_avail
@@ -325,6 +328,8 @@ bd_lvm_vgextend
 bd_lvm_vgreduce
 bd_lvm_vglock_start
 bd_lvm_vglock_stop
+bd_lvm_vgcfgbackup
+bd_lvm_vgcfgrestore
 bd_lvm_add_vg_tags
 bd_lvm_delete_vg_tags
 bd_lvm_vginfo
diff -pruN 3.3.1-3/include/blockdev/Makefile.am 3.4.0-1/include/blockdev/Makefile.am
--- 3.3.1-3/include/blockdev/Makefile.am	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/include/blockdev/Makefile.am	2025-09-24 13:46:42.000000000 +0000
@@ -1,5 +1,6 @@
 all-local:
 	for header in ${srcdir}/../../src/plugins/*.h; do ln -sf $${header} ./; done
+	for header in ${srcdir}/../../src/plugins/lvm/lvm.h; do ln -sf $${header} ./; done
 	for header in ${srcdir}/../../src/plugins/nvme/nvme.h; do ln -sf $${header} ./; done
 	for header in ${srcdir}/../../src/plugins/smart/smart.h; do ln -sf $${header} ./; done
 	for header in ${srcdir}/../../src/utils/*.h; do ln -sf $${header} ./; done
diff -pruN 3.3.1-3/misc/libblockdev-tasks.yml 3.4.0-1/misc/libblockdev-tasks.yml
--- 3.3.1-3/misc/libblockdev-tasks.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/misc/libblockdev-tasks.yml	2025-09-24 13:46:42.000000000 +0000
@@ -204,11 +204,17 @@
       - smartmontools
       - targetcli-fb
       - udftools
-      - vdo
       - volume-key
       - xfsprogs
   when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and test_dependencies|bool
 
+- name: Install vdo test dependencies (Debian/Ubuntu 64bit)
+  package:
+    state: present
+    name:
+      - vdo
+  when: (ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') and test_dependencies|bool and ansible_architecture != 'i386'
+
 ####### Common actions
 
 - name: Start LVM DBus service
diff -pruN 3.3.1-3/plans/blivet.fmf 3.4.0-1/plans/blivet.fmf
--- 3.3.1-3/plans/blivet.fmf	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/plans/blivet.fmf	2025-09-24 13:46:42.000000000 +0000
@@ -26,7 +26,7 @@ prepare:
 discover:
     how: shell
     url: https://github.com/storaged-project/blivet
-    ref: master
+    ref: main
     tests:
       - name: all
         test: make test
diff -pruN 3.3.1-3/plans/tests-rhel.fmf 3.4.0-1/plans/tests-rhel.fmf
--- 3.3.1-3/plans/tests-rhel.fmf	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/plans/tests-rhel.fmf	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,25 @@
+summary: Run tests
+
+adjust+:
+  - when: revdeps_blivet == yes or revdeps_udisks == yes
+    enabled: false
+  - when: distro == fedora
+    enabled: false
+
+prepare:
+  - name: copr
+    how: shell
+    script:
+      - sudo dnf install -y python3-libdnf5 'dnf-command(copr)'
+      - sudo dnf copr enable -y @storage/udisks-daily centos-stream-10-x86_64
+      # TF prioritizes Fedora tag repo over all others, in particular our daily COPR
+      - for f in $(grep -l -r 'testing-farm-tag-repository' /etc/yum.repos.d); do sed -i '/priority/d' "$f" ;done
+      - sudo dnf -y update
+
+  - name: ansible
+    how: ansible
+    playbook: misc/install-test-dependencies.yml
+
+execute:
+    how: tmt
+    script: ./autogen.sh && ./configure && make -j && sudo make ci
diff -pruN 3.3.1-3/plans/tests.fmf 3.4.0-1/plans/tests.fmf
--- 3.3.1-3/plans/tests.fmf	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/plans/tests.fmf	2025-09-24 13:46:42.000000000 +0000
@@ -1,8 +1,10 @@
 summary: Run tests
 
 adjust+:
-  when: revdeps_blivet == yes or revdeps_udisks == yes
-  enabled: false
+  - when: revdeps_blivet == yes or revdeps_udisks == yes
+    enabled: false
+  - when: distro == centos
+    enabled: false
 
 prepare:
   - name: copr
diff -pruN 3.3.1-3/src/lib/plugin_apis/crypto.api 3.4.0-1/src/lib/plugin_apis/crypto.api
--- 3.3.1-3/src/lib/plugin_apis/crypto.api	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/lib/plugin_apis/crypto.api	2025-09-24 13:46:42.000000000 +0000
@@ -380,6 +380,16 @@ typedef enum {
     BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW,
 } BDCryptoLUKSHWEncryptionType;
 
+typedef enum {
+    BD_CRYPTO_LUKS_ACTIVATE_ALLOW_DISCARDS          = 1 << 0,
+    BD_CRYPTO_LUKS_ACTIVATE_SAME_CPU_CRYPT          = 1 << 1,
+    BD_CRYPTO_LUKS_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS  = 1 << 2,
+    BD_CRYPTO_LUKS_ACTIVATE_NO_JOURNAL              = 1 << 3,
+    BD_CRYPTO_LUKS_ACTIVATE_NO_READ_WORKQUEUE       = 1 << 4,
+    BD_CRYPTO_LUKS_ACTIVATE_NO_WRITE_WORKQUEUE      = 1 << 5,
+    BD_CRYPTO_LUKS_ACTIVATE_HIGH_PRIORITY           = 1 << 6,
+} BDCryptoLUKSPersistentFlags;
+
 /**
  * BDCryptoLUKSInfo:
  * @version: LUKS version
@@ -1112,6 +1122,20 @@ gboolean bd_crypto_luks_set_uuid (const
 gboolean bd_crypto_luks_convert (const gchar *device, BDCryptoLUKSVersion target_version, GError **error);
 
 /**
+ * bd_crypto_luks_set_persistent_flags:
+ * @device: a LUKS device to set the persistent flags on
+ * @flags: flags to set
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Note: This function is valid only for LUKS2.
+ *
+ * Returns: whether the given @flags were successfully set or not
+ *
+ * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_MODIFY
+ */
+gboolean bd_crypto_luks_set_persistent_flags (const gchar *device, BDCryptoLUKSPersistentFlags flags, GError **error);
+
+/**
  * bd_crypto_luks_info:
  * @device: a device to get information about
  * @error: (out) (optional): place to store error (if any)
diff -pruN 3.3.1-3/src/lib/plugin_apis/loop.api 3.4.0-1/src/lib/plugin_apis/loop.api
--- 3.3.1-3/src/lib/plugin_apis/loop.api	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/lib/plugin_apis/loop.api	2025-09-24 13:46:42.000000000 +0000
@@ -185,4 +185,18 @@ gboolean bd_loop_teardown (const gchar *
  */
 gboolean bd_loop_set_autoclear (const gchar *loop, gboolean autoclear, GError **error);
 
+/**
+ * bd_loop_set_capacity:
+ * @loop: path or name of the loop device
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Force the loop driver to reread the size of the file associated with the
+ * specified @loop device.
+ *
+ * Returns: whether the LOOP_SET_CAPACITY ioctl was successfully issued or not.
+ *
+ * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_MODIFY
+ */
+gboolean bd_loop_set_capacity (const gchar *loop, GError **error);
+
 #endif  /* BD_LOOP_API */
diff -pruN 3.3.1-3/src/lib/plugin_apis/lvm.api 3.4.0-1/src/lib/plugin_apis/lvm.api
--- 3.3.1-3/src/lib/plugin_apis/lvm.api	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/lib/plugin_apis/lvm.api	2025-09-24 13:46:42.000000000 +0000
@@ -738,6 +738,7 @@ typedef enum {
     BD_LVM_TECH_DEVICES,
     BD_LVM_TECH_SHARED,
     BD_LVM_TECH_CONFIG,
+    BD_LVM_TECH_VG_CFG_BACKUP_RESTORE,
 } BDLVMTech;
 
 typedef enum {
@@ -2044,4 +2045,40 @@ gboolean bd_lvm_devices_delete (const gc
  */
 gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error);
 
+/**
+ * bd_lvm_vgcfgbackup:
+ * @vg_name: name of the VG to backup configuration
+ * @backup_file: (nullable): file to save the backup to or %NULL for using the default backup file
+ *                           in /etc/lvm/backup
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgbackup command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Note: This function does not back up the data content of LVs. See `vgcfbackup(8)` man page
+ *       for more information.
+ *
+ * Returns: Whether the backup was successfully created or not.
+ *
+ * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
+ */
+gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error);
+
+/**
+ * bd_lvm_vgcfgrestore:
+ * @vg_name: name of the VG to restore configuration
+ * @backup_file: (nullable): file to restore VG configuration from to or %NULL for using the
+ *                           latest backup in /etc/lvm/backup
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgrestore command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Note: This function restores VG configuration created by %bd_lvm_vgcfgbackup from given
+ *       @backup_file or from the latest backup in /etc/lvm/backup.
+ *
+ * Returns: Whether the configuration was successfully restored or not.
+ *
+ * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
+ */
+gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error);
+
 #endif  /* BD_LVM_API */
diff -pruN 3.3.1-3/src/lib/plugin_apis/nvme.api 3.4.0-1/src/lib/plugin_apis/nvme.api
--- 3.3.1-3/src/lib/plugin_apis/nvme.api	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/lib/plugin_apis/nvme.api	2025-09-24 13:46:42.000000000 +0000
@@ -1270,7 +1270,7 @@ gboolean bd_nvme_set_host_id (const gcha
  * @subsysnqn: The name for the NVMe subsystem to connect to.
  * @transport: The network fabric used for a NVMe-over-Fabrics network.
  * @transport_addr: (nullable): The network address of the Controller. For transports using IP addressing (e.g. `rdma`) this should be an IP-based address.
- * @transport_svcid: (nullable): The transport service id.  For transports using IP addressing (e.g. `rdma`) this field is the port number. By default, the IP port number for the `RDMA` transport is `4420`.
+ * @transport_svcid: (nullable): The transport service ID.  For transports using IP addressing (e.g. `tcp`, `rdma`) this field is the port number. The default port number for the `tcp` and `rdma` transports is `4420` and `8009` respectively when the well-known Discovery NQN is specified.
  * @host_traddr: (nullable): The network address used on the host to connect to the Controller. For TCP, this sets the source address on the socket.
  * @host_iface: (nullable): The network interface used on the host to connect to the Controller (e.g. IP `eth1`, `enp2s0`). This forces the connection to be made on a specific interface instead of letting the system decide.
  * @host_nqn: (nullable): Overrides the default Host NQN that identifies the NVMe Host. If this option is %NULL, the default is read from `/etc/nvme/hostnqn` first.
diff -pruN 3.3.1-3/src/plugins/Makefile.am 3.4.0-1/src/plugins/Makefile.am
--- 3.3.1-3/src/plugins/Makefile.am	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/Makefile.am	2025-09-24 13:46:42.000000000 +0000
@@ -8,6 +8,7 @@ if WITH_NVME
 SUBDIRS += nvme
 endif
 
+SUBDIRS += lvm
 SUBDIRS += smart
 
 lib_LTLIBRARIES =
@@ -28,14 +29,6 @@ if WITH_LOOP
 lib_LTLIBRARIES += libbd_loop.la
 endif
 
-if WITH_LVM
-lib_LTLIBRARIES += libbd_lvm.la
-endif
-
-if WITH_LVM_DBUS
-lib_LTLIBRARIES += libbd_lvm-dbus.la
-endif
-
 if WITH_MDRAID
 lib_LTLIBRARIES += libbd_mdraid.la
 endif
@@ -98,22 +91,6 @@ libbd_loop_la_CPPFLAGS = -I${builddir}/.
 libbd_loop_la_SOURCES = loop.c loop.h
 endif
 
-if WITH_LVM
-libbd_lvm_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) $(YAML_CFLAGS) -Wall -Wextra -Werror
-libbd_lvm_la_LIBADD = ${builddir}/../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) $(YAML_LIBS)
-libbd_lvm_la_LDFLAGS = -L${srcdir}/../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*'
-libbd_lvm_la_CPPFLAGS = -I${builddir}/../../include/
-libbd_lvm_la_SOURCES = lvm.c lvm.h check_deps.c check_deps.h dm_logging.c dm_logging.h vdo_stats.c vdo_stats.h
-endif
-
-if WITH_LVM_DBUS
-libbd_lvm_dbus_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) $(YAML_CFLAGS) -Wall -Wextra -Werror
-libbd_lvm_dbus_la_LIBADD = ${builddir}/../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) $(YAML_LIBS)
-libbd_lvm_dbus_la_LDFLAGS = -L${srcdir}/../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*'
-libbd_lvm_dbus_la_CPPFLAGS = -I${builddir}/../../include/
-libbd_lvm_dbus_la_SOURCES = lvm-dbus.c lvm.h check_deps.c check_deps.h dm_logging.c dm_logging.h vdo_stats.c vdo_stats.h
-endif
-
 if WITH_MDRAID
 libbd_mdraid_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(BYTESIZE_CFLAGS) -Wall -Wextra -Werror
 libbd_mdraid_la_LIBADD = ${builddir}/../utils/libbd_utils.la $(GLIB_LIBS) $(GIO_LIBS) $(BYTESIZE_LIBS)
@@ -182,14 +159,6 @@ if WITH_LOOP
 libinclude_HEADERS += loop.h
 endif
 
-if WITH_LVM
-libinclude_HEADERS += lvm.h
-else
-if WITH_LVM_DBUS
-libinclude_HEADERS += lvm.h
-endif
-endif
-
 if WITH_MDRAID
 libinclude_HEADERS += mdraid.h
 endif
diff -pruN 3.3.1-3/src/plugins/btrfs.c 3.4.0-1/src/plugins/btrfs.c
--- 3.3.1-3/src/plugins/btrfs.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/btrfs.c	2025-09-24 13:46:42.000000000 +0000
@@ -651,7 +651,7 @@ BDBtrfsSubvolumeInfo** bd_btrfs_list_sub
     gchar const * const pattern = "ID\\s+(?P<id>\\d+)\\s+gen\\s+\\d+\\s+(cgen\\s+\\d+\\s+)?" \
                                   "parent\\s+(?P<parent_id>\\d+)\\s+top\\s+level\\s+\\d+\\s+" \
                                   "(otime\\s+(\\d{4}-\\d{2}-\\d{2}\\s+\\d\\d:\\d\\d:\\d\\d|-)\\s+)?"\
-                                  "path\\s+(<FS_TREE>/)?(?P<path>\\S+)";
+                                  "path\\s+(<FS_TREE>/)?(?P<path>.+)";
     GRegex *regex = NULL;
     GMatchInfo *match_info = NULL;
     guint64 i = 0;
diff -pruN 3.3.1-3/src/plugins/crypto.c 3.4.0-1/src/plugins/crypto.c
--- 3.3.1-3/src/plugins/crypto.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/crypto.c	2025-09-24 13:46:42.000000000 +0000
@@ -950,6 +950,7 @@ gboolean _crypto_luks_format (const gcha
     gchar *msg = NULL;
     const gchar* crypt_version = NULL;
     GError *l_error = NULL;
+    struct crypt_pbkdf_type *pbkdf = NULL;
 
 #ifdef LIBCRYPTSETUP_27
     struct crypt_params_hw_opal opal_params = {
@@ -1068,7 +1069,7 @@ gboolean _crypto_luks_format (const gcha
     if (extra) {
         if (luks_version == BD_CRYPTO_LUKS_VERSION_LUKS1) {
 
-            if (extra->integrity || extra->sector_size || extra->label || extra->subsystem || extra->pbkdf) {
+            if (extra->integrity || extra->sector_size || extra->label || extra->subsystem) {
                 g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_PARAMS,
                              "Invalid extra arguments specified. Only `data_alignment`"
                              "and `data_device` are valid for LUKS 1.");
@@ -1079,11 +1080,35 @@ gboolean _crypto_luks_format (const gcha
                 return FALSE;
             }
 
+            if (extra->pbkdf) {
+                if (g_strcmp0 (extra->pbkdf->type, "pbkdf2") != 0) {
+                    g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_PARAMS,
+                                 "Invalid pbkdf specified. Only `pbkdf2` is valid for LUKS 1.");
+                crypt_free (cd);
+                g_strfreev (cipher_specs);
+                bd_utils_report_finished (progress_id, l_error->message);
+                g_propagate_error (error, l_error);
+                return FALSE;
+                }
+
+                pbkdf = get_pbkdf_params (extra->pbkdf, &l_error);
+                if (pbkdf == NULL && l_error != NULL) {
+                    crypt_free (cd);
+                    g_strfreev (cipher_specs);
+                    bd_utils_report_finished (progress_id, l_error->message);
+                    g_propagate_prefixed_error (error, l_error,
+                                                "Failed to get PBKDF parameters for '%s'.", device);
+                    return FALSE;
+                }
+                crypt_set_pbkdf_type (cd, pbkdf);
+            }
+
             struct crypt_params_luks1 params = ZERO_INIT;
             params.data_alignment = extra->data_alignment;
             params.data_device = extra->data_device;
             ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1],
                                 NULL, NULL, key_size, &params);
+            g_free (pbkdf);
         }
         else if (luks_version == BD_CRYPTO_LUKS_VERSION_LUKS2) {
             struct crypt_params_luks2 params = ZERO_INIT;
@@ -2289,6 +2314,82 @@ gboolean bd_crypto_luks_convert (const g
     return TRUE;
 }
 
+/**
+ * bd_crypto_luks_set_persistent_flags:
+ * @device: a LUKS device to set the persistent flags on
+ * @flags: flags to set
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Note: This function is valid only for LUKS2.
+ *
+ * Returns: whether the given @flags were successfully set or not
+ *
+ * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_MODIFY
+ */
+gboolean bd_crypto_luks_set_persistent_flags (const gchar *device, BDCryptoLUKSPersistentFlags flags, GError **error) {
+    struct crypt_device *cd = NULL;
+    gint ret = 0;
+    guint32 crypt_flags = 0;
+
+    ret = crypt_init (&cd, device);
+    if (ret != 0) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to initialize device: %s", strerror_l (-ret, c_locale));
+        return FALSE;
+    }
+
+    ret = crypt_load (cd, CRYPT_LUKS, NULL);
+    if (ret != 0) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to load device: %s", strerror_l (-ret, c_locale));
+        crypt_free (cd);
+        return FALSE;
+    }
+
+    if (g_strcmp0 (crypt_get_type (cd), CRYPT_LUKS2) != 0) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Persistent flags can be set only on LUKS v2");
+        crypt_free (cd);
+        return FALSE;
+    }
+
+    if (flags & BD_CRYPTO_LUKS_ACTIVATE_ALLOW_DISCARDS)
+        crypt_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
+    if (flags & BD_CRYPTO_LUKS_ACTIVATE_SAME_CPU_CRYPT)
+        crypt_flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT;
+    if (flags & BD_CRYPTO_LUKS_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)
+        crypt_flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
+    if (flags & BD_CRYPTO_LUKS_ACTIVATE_NO_JOURNAL)
+        crypt_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
+    if (flags & BD_CRYPTO_LUKS_ACTIVATE_NO_READ_WORKQUEUE)
+        crypt_flags |= CRYPT_ACTIVATE_NO_READ_WORKQUEUE;
+    if (flags & BD_CRYPTO_LUKS_ACTIVATE_NO_WRITE_WORKQUEUE)
+        crypt_flags |= CRYPT_ACTIVATE_NO_WRITE_WORKQUEUE;
+    if (flags & BD_CRYPTO_LUKS_ACTIVATE_HIGH_PRIORITY) {
+#ifdef LIBCRYPTSETUP_28
+        crypt_flags |= CRYPT_ACTIVATE_HIGH_PRIORITY;
+#else
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL,
+                     "Libcryptsetup 2.8 or newer is needed for 'high priority' flag support");
+        crypt_free (cd);
+        return FALSE;
+#endif
+    }
+
+
+    ret = crypt_persistent_flags_set (cd, CRYPT_FLAGS_ACTIVATION, crypt_flags);
+    if (ret != 0) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to set flags: %s", strerror_l (-ret, c_locale));
+        crypt_free (cd);
+        return FALSE;
+    }
+
+    crypt_free (cd);
+
+    return TRUE;
+}
+
 static gint synced_close (gint fd) {
     gint ret = 0;
     ret = fsync (fd);
diff -pruN 3.3.1-3/src/plugins/crypto.h 3.4.0-1/src/plugins/crypto.h
--- 3.3.1-3/src/plugins/crypto.h	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/crypto.h	2025-09-24 13:46:42.000000000 +0000
@@ -162,6 +162,16 @@ typedef enum {
     BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW,
 } BDCryptoLUKSHWEncryptionType;
 
+typedef enum {
+    BD_CRYPTO_LUKS_ACTIVATE_ALLOW_DISCARDS          = 1 << 0,
+    BD_CRYPTO_LUKS_ACTIVATE_SAME_CPU_CRYPT          = 1 << 1,
+    BD_CRYPTO_LUKS_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS  = 1 << 2,
+    BD_CRYPTO_LUKS_ACTIVATE_NO_JOURNAL              = 1 << 3,
+    BD_CRYPTO_LUKS_ACTIVATE_NO_READ_WORKQUEUE       = 1 << 4,
+    BD_CRYPTO_LUKS_ACTIVATE_NO_WRITE_WORKQUEUE      = 1 << 5,
+    BD_CRYPTO_LUKS_ACTIVATE_HIGH_PRIORITY           = 1 << 6,
+} BDCryptoLUKSPersistentFlags;
+
 /**
  * BDCryptoLUKSInfo:
  * @version: LUKS version
@@ -293,6 +303,7 @@ gboolean bd_crypto_luks_header_restore (
 gboolean bd_crypto_luks_set_label (const gchar *device, const gchar *label, const gchar *subsystem, GError **error);
 gboolean bd_crypto_luks_set_uuid (const gchar *device, const gchar *uuid, GError **error);
 gboolean bd_crypto_luks_convert (const gchar *device, BDCryptoLUKSVersion target_version, GError **error);
+gboolean bd_crypto_luks_set_persistent_flags (const gchar *device, BDCryptoLUKSPersistentFlags flags, GError **error);
 
 BDCryptoLUKSInfo* bd_crypto_luks_info (const gchar *device, GError **error);
 BDCryptoBITLKInfo* bd_crypto_bitlk_info (const gchar *device, GError **error);
diff -pruN 3.3.1-3/src/plugins/fs/generic.c 3.4.0-1/src/plugins/fs/generic.c
--- 3.3.1-3/src/plugins/fs/generic.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/fs/generic.c	2025-09-24 13:46:42.000000000 +0000
@@ -686,7 +686,9 @@ static gchar* fs_mount (const gchar *dev
             ret = bd_fs_mount (device, mountpoint, fstype, read_only ? "nosuid,nodev,ro" : "nosuid,nodev", NULL, &l_error);
             if (!ret) {
                 g_propagate_prefixed_error (error, l_error, "Failed to mount '%s': ", device);
-                g_rmdir (mountpoint);
+                if (g_rmdir (mountpoint) != 0)
+                    bd_utils_log_format (BD_UTILS_LOG_INFO, "Failed to remove temporary mountpoint '%s'",
+                                         mountpoint);
                 g_free (mountpoint);
                 return NULL;
             } else
@@ -703,6 +705,26 @@ static gchar* fs_mount (const gchar *dev
     return mountpoint;
 }
 
+static gboolean fs_unmount (const gchar *device, const gchar *mountpoint, const gchar *operation, GError **error) {
+    gboolean ret = FALSE;
+    GError *local_error = NULL;
+
+    ret = bd_fs_unmount (mountpoint, FALSE, FALSE, NULL, &local_error);
+    if (!ret) {
+        g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNMOUNT_FAIL,
+                     "Failed to unmount '%s' after %s it: %s",
+                     device, operation, local_error->message);
+        g_clear_error (&local_error);
+        return FALSE;
+    } else {
+        if (g_rmdir (mountpoint) != 0)
+            bd_utils_log_format (BD_UTILS_LOG_INFO, "Failed to remove temporary mountpoint '%s'",
+                                 mountpoint);
+    }
+    return TRUE;
+}
+
+
 /**
  * xfs_resize_device:
  * @device: the device the file system of which to resize
@@ -739,22 +761,18 @@ static gboolean xfs_resize_device (const
     success = bd_fs_xfs_resize (mountpoint, new_size, extra, error);
 
     if (unmount) {
-        ret = bd_fs_unmount (mountpoint, FALSE, FALSE, NULL, &local_error);
+        ret = fs_unmount (device, mountpoint, "resizing", &local_error);
         if (!ret) {
             if (success) {
                 /* resize was successful but unmount failed */
-                g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNMOUNT_FAIL,
-                             "Failed to unmount '%s' after resizing it: %s",
-                             device, local_error->message);
-                g_clear_error (&local_error);
+                g_propagate_error (error, local_error);
                 return FALSE;
             } else
                 /* both resize and unmount were unsuccessful but the error
                    from the resize is more important so just ignore the
                    unmount error */
                 g_clear_error (&local_error);
-        } else
-            g_rmdir (mountpoint);
+        }
     }
 
     return success;
@@ -794,22 +812,18 @@ static gboolean nilfs2_resize_device (co
     success = bd_fs_nilfs2_resize (device, new_size, error);
 
     if (unmount) {
-        ret = bd_fs_unmount (mountpoint, FALSE, FALSE, NULL, &local_error);
+        ret = fs_unmount (device, mountpoint, "resizing", &local_error);
         if (!ret) {
             if (success) {
                 /* resize was successful but unmount failed */
-                g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNMOUNT_FAIL,
-                             "Failed to unmount '%s' after resizing it: %s",
-                             device, local_error->message);
-                g_clear_error (&local_error);
+                g_propagate_error (error, local_error);
                 return FALSE;
             } else
                 /* both resize and unmount were unsuccessful but the error
                    from the resize is more important so just ignore the
                    unmount error */
                 g_clear_error (&local_error);
-        } else
-            g_rmdir (mountpoint);
+        }
     }
 
     return success;
@@ -829,14 +843,11 @@ static BDFSBtrfsInfo* btrfs_get_info (co
     btrfs_info = bd_fs_btrfs_get_info (mountpoint, error);
 
     if (unmount) {
-        ret = bd_fs_unmount (mountpoint, FALSE, FALSE, NULL, &local_error);
+        ret = fs_unmount (device, mountpoint, "getting info", &local_error);
         if (!ret) {
             if (btrfs_info) {
                 /* info was successful but unmount failed */
-                g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNMOUNT_FAIL,
-                             "Failed to unmount '%s' after getting info: %s",
-                             device, local_error->message);
-                g_clear_error (&local_error);
+                g_propagate_error (error, local_error);
                 bd_fs_btrfs_info_free (btrfs_info);
                 return NULL;
             } else
@@ -844,8 +855,7 @@ static BDFSBtrfsInfo* btrfs_get_info (co
                    from the info is more important so just ignore the
                    unmount error */
                 g_clear_error (&local_error);
-        } else
-            g_rmdir (mountpoint);
+        }
     }
 
     return btrfs_info;
@@ -865,22 +875,18 @@ static gboolean btrfs_resize_device (con
     success = bd_fs_btrfs_resize (mountpoint, new_size, NULL, error);
 
     if (unmount) {
-        ret = bd_fs_unmount (mountpoint, FALSE, FALSE, NULL, &local_error);
+        ret = fs_unmount (device, mountpoint, "resizing", &local_error);
         if (!ret) {
             if (success) {
                 /* resize was successful but unmount failed */
-                g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNMOUNT_FAIL,
-                             "Failed to unmount '%s' after resizing it: %s",
-                             device, local_error->message);
-                g_clear_error (&local_error);
+                g_propagate_error (error, local_error);
                 return FALSE;
             } else
                 /* both resize and unmount were unsuccessful but the error
                    from the resize is more important so just ignore the
                    unmount error */
                 g_clear_error (&local_error);
-        } else
-            g_rmdir (mountpoint);
+        }
     }
 
     return success;
@@ -900,22 +906,18 @@ static gboolean btrfs_set_label (const g
     success = bd_fs_btrfs_set_label (mountpoint, label, error);
 
     if (unmount) {
-        ret = bd_fs_unmount (mountpoint, FALSE, FALSE, NULL, &local_error);
+        ret = fs_unmount (device, mountpoint, "setting label", &local_error);
         if (!ret) {
             if (success) {
-                /* resize was successful but unmount failed */
-                g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNMOUNT_FAIL,
-                             "Failed to unmount '%s' after setting label: %s",
-                             device, local_error->message);
-                g_clear_error (&local_error);
+                /* label was successful but unmount failed */
+                g_propagate_error (error, local_error);
                 return FALSE;
             } else
-                /* both set label and unmount were unsuccessful but the error
-                   from the set label is more important so just ignore the
+                /* both label and unmount were unsuccessful but the error
+                   from the label is more important so just ignore the
                    unmount error */
                 g_clear_error (&local_error);
-        } else
-            g_rmdir (mountpoint);
+        }
     }
 
     return success;
diff -pruN 3.3.1-3/src/plugins/fs/xfs.c 3.4.0-1/src/plugins/fs/xfs.c
--- 3.3.1-3/src/plugins/fs/xfs.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/fs/xfs.c	2025-09-24 13:46:42.000000000 +0000
@@ -398,11 +398,23 @@ BDFSXfsInfo* bd_fs_xfs_get_info (const g
 
     /* extract data from something like this: "data     =      bsize=4096   blocks=262400, imaxpct=25" */
     val_start = strchr (*line_p, '=');
+    if (!val_start) {
+        g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
+        g_strfreev (lines);
+        bd_fs_xfs_info_free (ret);
+        return NULL;
+    }
     val_start++;
     while (isspace (*val_start))
         val_start++;
     if (g_str_has_prefix (val_start, "bsize")) {
         val_start = strchr (val_start, '=');
+        if (!val_start) {
+            g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
+            g_strfreev (lines);
+            bd_fs_xfs_info_free (ret);
+            return NULL;
+        }
         val_start++;
         ret->block_size = g_ascii_strtoull (val_start, NULL, 0);
     } else {
@@ -415,6 +427,12 @@ BDFSXfsInfo* bd_fs_xfs_get_info (const g
         val_start++;
     if (g_str_has_prefix (val_start, "blocks")) {
         val_start = strchr (val_start, '=');
+        if (!val_start) {
+            g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_PARSE, "Failed to parse xfs file system information");
+            g_strfreev (lines);
+            bd_fs_xfs_info_free (ret);
+            return NULL;
+        }
         val_start++;
         ret->block_count = g_ascii_strtoull (val_start, NULL, 0);
     } else {
diff -pruN 3.3.1-3/src/plugins/loop.c 3.4.0-1/src/plugins/loop.c
--- 3.3.1-3/src/plugins/loop.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/loop.c	2025-09-24 13:46:42.000000000 +0000
@@ -517,3 +517,64 @@ gboolean bd_loop_set_autoclear (const gc
     bd_utils_report_finished (progress_id, "Completed");
     return TRUE;
 }
+
+/**
+ * bd_loop_set_capacity:
+ * @loop: path or name of the loop device
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Force the loop driver to reread the size of the file associated with the
+ * specified @loop device.
+ *
+ * Returns: whether the LOOP_SET_CAPACITY ioctl was successfully issued or not.
+ *
+ * Tech category: %BD_LOOP_TECH_LOOP-%BD_LOOP_TECH_MODE_MODIFY
+ */
+gboolean bd_loop_set_capacity (const gchar *loop, GError **error) {
+    gchar *dev_loop = NULL;
+    gint fd = -1;
+    guint64 progress_id = 0;
+    gchar *msg = NULL;
+    guint n_try = 0;
+    gint status = 0;
+    GError *l_error = NULL;
+
+    if (!g_str_has_prefix (loop, "/dev/"))
+        dev_loop = g_strdup_printf ("/dev/%s", loop);
+
+    msg = g_strdup_printf ("Started setting up capacity on the %s device",
+                           dev_loop ? dev_loop : loop);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    fd = open (dev_loop ? dev_loop : loop, O_RDWR);
+    g_free (dev_loop);
+    if (fd < 0) {
+        g_set_error (&l_error, BD_LOOP_ERROR, BD_LOOP_ERROR_DEVICE,
+                     "Failed to open device %s: %m", loop);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    for (n_try=10, status=-1; (status != 0) && (n_try > 0); n_try--) {
+        status = ioctl (fd, LOOP_SET_CAPACITY, 0);
+        if (status < 0 && errno == EAGAIN)
+            g_usleep (100 * 1000); /* microseconds */
+        else
+            break;
+    }
+
+    if (status != 0) {
+        g_set_error (&l_error, BD_LOOP_ERROR, BD_LOOP_ERROR_FAIL,
+                     "Failed to set capacity of the device %s: %m", loop);
+        close (fd);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    close (fd);
+    bd_utils_report_finished (progress_id, "Completed");
+    return TRUE;
+}
diff -pruN 3.3.1-3/src/plugins/loop.h 3.4.0-1/src/plugins/loop.h
--- 3.3.1-3/src/plugins/loop.h	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/loop.h	2025-09-24 13:46:42.000000000 +0000
@@ -66,5 +66,6 @@ gboolean bd_loop_setup_from_fd (gint fd,
 gboolean bd_loop_teardown (const gchar *loop, GError **error);
 
 gboolean bd_loop_set_autoclear (const gchar *loop, gboolean autoclear, GError **error);
+gboolean bd_loop_set_capacity (const gchar *loop, GError **error);
 
 #endif  /* BD_LOOP */
diff -pruN 3.3.1-3/src/plugins/lvm/Makefile.am 3.4.0-1/src/plugins/lvm/Makefile.am
--- 3.3.1-3/src/plugins/lvm/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/Makefile.am	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,59 @@
+AUTOMAKE_OPTIONS = subdir-objects
+
+lib_LTLIBRARIES =
+libincludedir = $(includedir)/blockdev
+
+if WITH_LVM
+libinclude_HEADERS = lvm.h
+else
+if WITH_LVM_DBUS
+libinclude_HEADERS = lvm.h
+endif
+endif
+
+
+if WITH_LVM
+
+lib_LTLIBRARIES += libbd_lvm.la
+
+libbd_lvm_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) $(YAML_CFLAGS) -Wall -Wextra -Werror
+libbd_lvm_la_LIBADD = ${builddir}/../../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) $(YAML_LIBS)
+libbd_lvm_la_LDFLAGS = -L${srcdir}/../../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*'
+libbd_lvm_la_CPPFLAGS = -I${builddir}/../../../include/ -I${srcdir}/../ -I. -DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\"
+
+libbd_lvm_la_SOURCES = \
+	lvm.c \
+	lvm.h \
+	lvm-private.h \
+	lvm-common.c \
+	vdo_stats.c \
+	vdo_stats.h \
+	../check_deps.c \
+	../check_deps.h \
+	../dm_logging.c \
+	../dm_logging.h
+
+endif
+
+if WITH_LVM_DBUS
+
+lib_LTLIBRARIES += libbd_lvm-dbus.la
+
+libbd_lvm_dbus_la_CFLAGS = $(GLIB_CFLAGS) $(GIO_CFLAGS) $(DEVMAPPER_CFLAGS) $(YAML_CFLAGS) -Wall -Wextra -Werror
+libbd_lvm_dbus_la_LIBADD = ${builddir}/../../utils/libbd_utils.la -lm $(GLIB_LIBS) $(GIO_LIBS) $(DEVMAPPER_LIBS) $(YAML_LIBS)
+libbd_lvm_dbus_la_LDFLAGS = -L${srcdir}/../../utils/ -version-info 3:0:0 -Wl,--no-undefined -export-symbols-regex '^bd_.*'
+libbd_lvm_dbus_la_CPPFLAGS = -I${builddir}/../../../include/ -I${srcdir}/../ -I. -DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\"
+
+libbd_lvm_dbus_la_SOURCES = \
+	lvm-dbus.c \
+	lvm.h \
+	lvm-private.h \
+	lvm-common.c \
+	vdo_stats.c \
+	vdo_stats.h \
+	../check_deps.c \
+	../check_deps.h \
+	../dm_logging.c \
+	../dm_logging.h
+
+endif
diff -pruN 3.3.1-3/src/plugins/lvm/lvm-common.c 3.4.0-1/src/plugins/lvm/lvm-common.c
--- 3.3.1-3/src/plugins/lvm/lvm-common.c	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/lvm-common.c	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,852 @@
+/*
+ * Copyright (C) 2014-2025  Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <glib.h>
+#include <math.h>
+#include <stdio.h>
+#include <libdevmapper.h>
+
+#include "lvm.h"
+#include "lvm-private.h"
+#include "check_deps.h"
+#include "dm_logging.h"
+#include "vdo_stats.h"
+
+
+#define INT_FLOAT_EPS 1e-5
+
+#define MIN_PE_SIZE (1 KiB)
+#define MAX_PE_SIZE (16 GiB)
+
+#define VDO_POOL_SUFFIX "vpool"
+
+#define THPOOL_MD_FACTOR_NEW (0.2)
+#define THPOOL_MD_FACTOR_EXISTS (1 / 6.0)
+
+#define MIN_THPOOL_MD_SIZE (4 MiB)
+/* DM_THIN_MAX_METADATA_SIZE is in 512 sectors */
+#define MAX_THPOOL_MD_SIZE (DM_THIN_MAX_METADATA_SIZE * 512)
+
+#define MIN_THPOOL_CHUNK_SIZE (64 KiB)
+#define MAX_THPOOL_CHUNK_SIZE (1 GiB)
+#define DEFAULT_CHUNK_SIZE (64 KiB)
+
+/* according to lvmcache (7) */
+#define MIN_CACHE_MD_SIZE (8 MiB)
+
+#ifdef __LP64__
+/* 64bit system */
+#define MAX_LV_SIZE (8 EiB)
+#else
+/* 32bit system */
+#define MAX_LV_SIZE (16 TiB)
+#endif
+
+GMutex global_config_lock;
+gchar *global_config_str = NULL;
+gchar *global_devices_str = NULL;
+
+/**
+ * bd_lvm_is_supported_pe_size:
+ * @size: size (in bytes) to test
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the given size is supported physical extent size or not
+ *
+ * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
+ */
+gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error G_GNUC_UNUSED) {
+    return (((size % 2) == 0) && (size >= (MIN_PE_SIZE)) && (size <= (MAX_PE_SIZE)));
+}
+
+/**
+ * bd_lvm_get_supported_pe_sizes:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full) (array fixed-size=25): list of supported PE sizes
+ *
+ * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
+ */
+guint64 *bd_lvm_get_supported_pe_sizes (GError **error G_GNUC_UNUSED) {
+    guint8 i;
+    guint64 val = MIN_PE_SIZE;
+    guint8 num_items = ((guint8) round (log2 ((double) MAX_PE_SIZE))) - ((guint8) round (log2 ((double) MIN_PE_SIZE))) + 2;
+    guint64 *ret = g_new0 (guint64, num_items);
+
+    for (i=0; (val <= MAX_PE_SIZE); i++, val = val * 2)
+        ret[i] = val;
+
+    ret[num_items-1] = 0;
+
+    return ret;
+}
+
+/**
+ * bd_lvm_get_max_lv_size:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: maximum LV size in bytes
+ *
+ * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
+ */
+guint64 bd_lvm_get_max_lv_size (GError **error G_GNUC_UNUSED) {
+    return MAX_LV_SIZE;
+}
+
+/**
+ * bd_lvm_round_size_to_pe:
+ * @size: size to be rounded
+ * @pe_size: physical extent (PE) size or 0 to use the default
+ * @roundup: whether to round up or down (ceil or floor)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: @size rounded to @pe_size according to the @roundup
+ *
+ * Rounds given @size up/down to a multiple of @pe_size according to the value
+ * of the @roundup parameter. If the rounded value is too big to fit in the
+ * return type, the result is rounded down (floored) regardless of the @roundup
+ * parameter.
+ *
+ * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
+ */
+guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error G_GNUC_UNUSED) {
+    pe_size = RESOLVE_PE_SIZE (pe_size);
+    guint64 delta = size % pe_size;
+    if (delta == 0)
+        return size;
+
+    if (roundup && (((G_MAXUINT64 - (pe_size - delta)) >= size)))
+        return size + (pe_size - delta);
+    else
+        return size - delta;
+}
+
+/**
+ * bd_lvm_get_lv_physical_size:
+ * @lv_size: LV size
+ * @pe_size: PE size
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: space taken on disk(s) by the LV with given @size
+ *
+ * Gives number of bytes needed for an LV with the size @lv_size on an LVM stack
+ * using given @pe_size.
+ *
+ * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
+ */
+guint64 bd_lvm_get_lv_physical_size (guint64 lv_size, guint64 pe_size, GError **error) {
+    pe_size = RESOLVE_PE_SIZE (pe_size);
+
+    /* the LV just takes space rounded up to the multiple of extent size */
+    return bd_lvm_round_size_to_pe (lv_size, pe_size, TRUE, error);
+}
+
+/**
+ * bd_lvm_get_thpool_padding:
+ * @size: size of the thin pool
+ * @pe_size: PE size or 0 if the default value should be used
+ * @included: if padding is already included in the size
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: size of the padding needed for a thin pool with the given @size
+ *         according to the @pe_size and @included
+ *
+ * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
+ */
+guint64 bd_lvm_get_thpool_padding (guint64 size, guint64 pe_size, gboolean included, GError **error) {
+    guint64 raw_md_size;
+    pe_size = RESOLVE_PE_SIZE (pe_size);
+
+    if (included)
+        raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_EXISTS);
+    else
+        raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_NEW);
+
+    return MIN (bd_lvm_round_size_to_pe (raw_md_size, pe_size, TRUE, error),
+                bd_lvm_round_size_to_pe (MAX_THPOOL_MD_SIZE, pe_size, TRUE, error));
+}
+
+/**
+ * bd_lvm_get_thpool_meta_size:
+ * @size: size of the thin pool
+ * @chunk_size: chunk size of the thin pool or 0 to use the default
+ * @n_snapshots: ignored
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Note: This function will be changed in 3.0: the @n_snapshots parameter
+ *       is currently not used and will be removed.
+ *
+ * Returns: recommended size of the metadata space for the specified pool
+ *
+ * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
+ */
+guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) {
+    guint64 md_size = 0;
+
+    /* based on lvcreate metadata size calculation */
+    md_size = UINT64_C (64) * size / (chunk_size ? chunk_size : DEFAULT_CHUNK_SIZE);
+
+    if (md_size > MAX_THPOOL_MD_SIZE)
+        md_size = MAX_THPOOL_MD_SIZE;
+    else if (md_size < MIN_THPOOL_MD_SIZE)
+        md_size = MIN_THPOOL_MD_SIZE;
+
+    return md_size;
+}
+
+/**
+ * bd_lvm_is_valid_thpool_md_size:
+ * @size: the size to be tested
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the given size is a valid thin pool metadata size or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
+ */
+gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error G_GNUC_UNUSED) {
+    return ((MIN_THPOOL_MD_SIZE <= size) && (size <= MAX_THPOOL_MD_SIZE));
+}
+
+/**
+ * bd_lvm_is_valid_thpool_chunk_size:
+ * @size: the size to be tested
+ * @discard: whether discard/TRIM is required to be supported or not
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the given size is a valid thin pool chunk size or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
+ */
+gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error G_GNUC_UNUSED) {
+    gdouble size_log2 = 0.0;
+
+    if ((size < MIN_THPOOL_CHUNK_SIZE) || (size > MAX_THPOOL_CHUNK_SIZE))
+        return FALSE;
+
+    /* To support discard, chunk size must be a power of two. Otherwise it must be a
+       multiple of 64 KiB. */
+    if (discard) {
+        size_log2 = log2 ((double) size);
+        return ABS (((int) round (size_log2)) - size_log2) <= INT_FLOAT_EPS;
+    } else
+        return (size % (64 KiB)) == 0;
+}
+
+/**
+ * bd_lvm_cache_get_default_md_size:
+ * @cache_size: size of the cache to determine MD size for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: recommended default size of the cache metadata LV or 0 in case of error
+ *
+ * Tech category: %BD_LVM_TECH_CACHE_CALCS no mode (it is ignored)
+ */
+guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error G_GNUC_UNUSED) {
+    return MAX ((guint64) cache_size / 1000, MIN_CACHE_MD_SIZE);
+}
+
+/**
+ * bd_lvm_set_global_config:
+ * @new_config: (nullable): string representation of the new global libblockdev LVM
+ *                          configuration to set or %NULL to reset to default
+ * @error: (out) (optional): place to store error (if any)
+ *
+ *
+ * Note: This function sets configuration options for LVM calls internally
+ *       in libblockdev, it doesn't change the global lvm.conf config file.
+ *       Calling this function with `backup {backup=0 archive=0}` for example
+ *       means `--config=backup {backup=0 archive=0}"` will be added to all
+ *       calls libblockdev makes.
+ *
+ * Returns: whether the new requested global config @new_config was successfully
+ *          set or not
+ *
+ * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
+ */
+gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error G_GNUC_UNUSED) {
+    /* XXX: the error attribute will likely be used in the future when
+       some validation comes into the game */
+
+    g_mutex_lock (&global_config_lock);
+
+    /* first free the old value */
+    g_free (global_config_str);
+
+    /* now store the new one */
+    if (!new_config || g_strcmp0 (new_config, "") == 0)
+         global_config_str = NULL;
+    else
+        global_config_str = g_strdup (new_config);
+
+    g_mutex_unlock (&global_config_lock);
+    return TRUE;
+}
+
+/**
+ * bd_lvm_get_global_config:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): a copy of a string representation of the currently
+ *                           set libblockdev LVM global configuration
+ *
+ * Note: This function does not change the global `lvm.conf` config
+ *       file, see %bd_lvm_set_global_config for details.
+ *
+ * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
+ */
+gchar* bd_lvm_get_global_config (GError **error G_GNUC_UNUSED) {
+    gchar *ret = NULL;
+
+    g_mutex_lock (&global_config_lock);
+    ret = g_strdup (global_config_str ? global_config_str : "");
+    g_mutex_unlock (&global_config_lock);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_set_devices_filter:
+ * @devices: (nullable) (array zero-terminated=1): list of devices for lvm commands to work on
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the devices filter was successfully set or not
+ *
+ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
+ */
+gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error) {
+    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
+        return FALSE;
+
+    g_mutex_lock (&global_config_lock);
+
+    /* first free the old value */
+    g_free (global_devices_str);
+
+    /* now store the new one */
+    if (!devices || !(*devices))
+        global_devices_str = NULL;
+    else
+        global_devices_str = g_strjoinv (",", (gchar **) devices);
+
+    g_mutex_unlock (&global_config_lock);
+    return TRUE;
+}
+
+/**
+ * bd_lvm_get_devices_filter:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
+ *                                                     the currently set LVM devices filter
+ *
+ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
+ */
+gchar** bd_lvm_get_devices_filter (GError **error G_GNUC_UNUSED) {
+    gchar **ret = NULL;
+
+    g_mutex_lock (&global_config_lock);
+
+    if (global_devices_str)
+        ret = g_strsplit (global_devices_str, ",", -1);
+    else
+        ret = NULL;
+
+    g_mutex_unlock (&global_config_lock);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_cache_get_mode_str:
+ * @mode: mode to get the string representation for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: string representation of @mode or %NULL in case of error
+ *
+ * Tech category: always provided/supported
+ */
+const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error) {
+    if (mode == BD_LVM_CACHE_MODE_WRITETHROUGH)
+        return "writethrough";
+    else if (mode == BD_LVM_CACHE_MODE_WRITEBACK)
+        return "writeback";
+    else if (mode == BD_LVM_CACHE_MODE_UNKNOWN)
+        return "unknown";
+    else {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Invalid mode given: %d", mode);
+        return NULL;
+    }
+}
+
+/**
+ * bd_lvm_cache_get_mode_from_str:
+ * @mode_str: string representation of a cache mode
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: cache mode for the @mode_str or %BD_LVM_CACHE_MODE_UNKNOWN if
+ *          failed to determine
+ *
+ * Tech category: always provided/supported
+ */
+BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error) {
+    if (g_strcmp0 (mode_str, "writethrough") == 0)
+        return BD_LVM_CACHE_MODE_WRITETHROUGH;
+    else if (g_strcmp0 (mode_str, "writeback") == 0)
+        return BD_LVM_CACHE_MODE_WRITEBACK;
+    else if (g_strcmp0 (mode_str, "unknown") == 0)
+        return BD_LVM_CACHE_MODE_UNKNOWN;
+    else {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Invalid mode given: %s", mode_str);
+        return BD_LVM_CACHE_MODE_UNKNOWN;
+    }
+}
+
+/**
+ * bd_lvm_get_vdo_operating_mode_str:
+ * @mode: mode to get the string representation for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: string representation of @mode or %NULL in case of error
+ *
+ * Tech category: always provided/supported
+ */
+const gchar* bd_lvm_get_vdo_operating_mode_str (BDLVMVDOOperatingMode mode, GError **error) {
+    switch (mode) {
+    case BD_LVM_VDO_MODE_RECOVERING:
+        return "recovering";
+    case BD_LVM_VDO_MODE_READ_ONLY:
+        return "read-only";
+    case BD_LVM_VDO_MODE_NORMAL:
+        return "normal";
+    case BD_LVM_VDO_MODE_UNKNOWN:
+        return "unknown";
+    default:
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Invalid LVM VDO operating mode.");
+        return NULL;
+    }
+}
+
+/**
+ * bd_lvm_get_vdo_compression_state_str:
+ * @state: state to get the string representation for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: string representation of @state or %NULL in case of error
+ *
+ * Tech category: always provided/supported
+ */
+const gchar* bd_lvm_get_vdo_compression_state_str (BDLVMVDOCompressionState state, GError **error) {
+    switch (state) {
+    case BD_LVM_VDO_COMPRESSION_ONLINE:
+        return "online";
+    case BD_LVM_VDO_COMPRESSION_OFFLINE:
+        return "offline";
+    case BD_LVM_VDO_COMPRESSION_UNKNOWN:
+        return "unknown";
+    default:
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Invalid LVM VDO compression state.");
+        return NULL;
+    }
+}
+
+/**
+ * bd_lvm_get_vdo_index_state_str:
+ * @state: state to get the string representation for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: string representation of @state or %NULL in case of error
+ *
+ * Tech category: always provided/supported
+ */
+const gchar* bd_lvm_get_vdo_index_state_str (BDLVMVDOIndexState state, GError **error) {
+    switch (state) {
+    case BD_LVM_VDO_INDEX_ERROR:
+        return "error";
+    case BD_LVM_VDO_INDEX_CLOSED:
+        return "closed";
+    case BD_LVM_VDO_INDEX_OPENING:
+        return "opening";
+    case BD_LVM_VDO_INDEX_CLOSING:
+        return "closing";
+    case BD_LVM_VDO_INDEX_OFFLINE:
+        return "offline";
+    case BD_LVM_VDO_INDEX_ONLINE:
+        return "online";
+    case BD_LVM_VDO_INDEX_UNKNOWN:
+        return "unknown";
+    default:
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Invalid LVM VDO index state.");
+        return NULL;
+    }
+}
+
+/**
+ * bd_lvm_get_vdo_write_policy_str:
+ * @policy: policy to get the string representation for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: string representation of @policy or %NULL in case of error
+ *
+ * Tech category: always provided/supported
+ */
+const gchar* bd_lvm_get_vdo_write_policy_str (BDLVMVDOWritePolicy policy, GError **error) {
+    switch (policy) {
+    case BD_LVM_VDO_WRITE_POLICY_AUTO:
+        return "auto";
+    case BD_LVM_VDO_WRITE_POLICY_SYNC:
+        return "sync";
+    case BD_LVM_VDO_WRITE_POLICY_ASYNC:
+        return "async";
+    case BD_LVM_VDO_WRITE_POLICY_UNKNOWN:
+        return "unknown";
+    default:
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Invalid LVM VDO write policy.");
+        return NULL;
+    }
+}
+
+/**
+ * bd_lvm_get_vdo_write_policy_from_str:
+ * @policy_str: string representation of a policy
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: write policy for the @policy_str or %BD_LVM_VDO_WRITE_POLICY_UNKNOWN if
+ *          failed to determine
+ *
+ * Tech category: always provided/supported
+ */
+BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_str, GError **error) {
+    if (g_strcmp0 (policy_str, "auto") == 0)
+        return BD_LVM_VDO_WRITE_POLICY_AUTO;
+    else if (g_strcmp0 (policy_str, "sync") == 0)
+        return BD_LVM_VDO_WRITE_POLICY_SYNC;
+    else if (g_strcmp0 (policy_str, "async") == 0)
+        return BD_LVM_VDO_WRITE_POLICY_ASYNC;
+    else {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_VDO_POLICY_INVAL,
+                     "Invalid policy given: %s", policy_str);
+        return BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
+    }
+}
+
+/**
+ * bd_lvm_vdo_get_stats_full:
+ * @vg_name: name of the VG that contains @pool_name VDO pool
+ * @pool_name: name of the VDO pool to get statistics for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full) (element-type utf8 utf8): hashtable of type string - string of available
+ *                                                    statistics or %NULL in case of error
+ *                                                    (@error gets populated in those cases)
+ *
+ * Statistics are collected from the values exposed by the kernel `dm-vdo` module.
+ *
+ * Some of the keys are computed to mimic the information produced by the vdo tools.
+ * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version.
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
+ */
+GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error) {
+    g_autofree gchar *kvdo_name = g_strdup_printf ("%s-%s-%s", vg_name, pool_name, VDO_POOL_SUFFIX);
+    return vdo_get_stats_full (kvdo_name, error);
+}
+
+/**
+ * bd_lvm_vdo_get_stats:
+ * @vg_name: name of the VG that contains @pool_name VDO pool
+ * @pool_name: name of the VDO pool to get statistics for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): a structure containing selected statistics or %NULL in case of error
+ *                           (@error gets populated in those cases)
+ *
+ * In contrast to @bd_lvm_vdo_get_stats_full this function will only return selected statistics
+ * in a fixed structure. In case a value is not available, -1 would be returned.
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error) {
+    GHashTable *full_stats = NULL;
+    BDLVMVDOStats *stats = NULL;
+
+    full_stats = bd_lvm_vdo_get_stats_full (vg_name, pool_name, error);
+    if (!full_stats)
+        return NULL;
+
+    stats = g_new0 (BDLVMVDOStats, 1);
+    get_stat_val64_default (full_stats, "blockSize", &stats->block_size, -1);
+    get_stat_val64_default (full_stats, "logicalBlockSize", &stats->logical_block_size, -1);
+    get_stat_val64_default (full_stats, "physicalBlocks", &stats->physical_blocks, -1);
+    get_stat_val64_default (full_stats, "dataBlocksUsed", &stats->data_blocks_used, -1);
+    get_stat_val64_default (full_stats, "overheadBlocksUsed", &stats->overhead_blocks_used, -1);
+    get_stat_val64_default (full_stats, "logicalBlocksUsed", &stats->logical_blocks_used, -1);
+    get_stat_val64_default (full_stats, "usedPercent", &stats->used_percent, -1);
+    get_stat_val64_default (full_stats, "savingPercent", &stats->saving_percent, -1);
+    if (!get_stat_val_double (full_stats, "writeAmplificationRatio", &stats->write_amplification_ratio))
+        stats->write_amplification_ratio = -1;
+
+    g_hash_table_destroy (full_stats);
+
+    return stats;
+}
+
+/* check whether the LVM devices file is enabled by LVM
+ * we use the existence of the "lvmdevices" command to check whether the feature is available
+ * or not, but this can still be disabled either in LVM or in lvm.conf
+ */
+static gboolean _lvm_devices_enabled () {
+    const gchar *args[6] = {"lvmconfig", "--typeconfig", NULL, "devices/use_devicesfile", NULL, NULL};
+    gboolean ret = FALSE;
+    GError *loc_error = NULL;
+    gchar *output = NULL;
+    gboolean enabled = FALSE;
+    gint scanned = 0;
+    g_autofree gchar *config_arg = NULL;
+
+    /* try full config first -- if we get something from this it means the feature is
+       explicitly enabled or disabled by system lvm.conf or using the --config option */
+    args[2] = "full";
+
+    /* make sure to include the global config from us when getting the current config value */
+    g_mutex_lock (&global_config_lock);
+    if (global_config_str) {
+        config_arg = g_strdup_printf ("--config=%s", global_config_str);
+        args[4] = config_arg;
+    }
+
+    ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
+    g_mutex_unlock (&global_config_lock);
+    if (ret) {
+        scanned = sscanf (output, "use_devicesfile=%u", &enabled);
+        g_free (output);
+        if (scanned != 1)
+            return FALSE;
+
+        return enabled;
+    } else {
+        g_clear_error (&loc_error);
+        g_free (output);
+    }
+
+    output = NULL;
+
+    /* now try default */
+    args[2] = "default";
+    ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
+    if (ret) {
+        scanned = sscanf (output, "# use_devicesfile=%u", &enabled);
+        g_free (output);
+        if (scanned != 1)
+            return FALSE;
+
+        return enabled;
+    } else {
+        g_clear_error (&loc_error);
+        g_free (output);
+    }
+
+    return FALSE;
+}
+
+/**
+ * bd_lvm_devices_add:
+ * @device: device (PV) to add to the devices file
+ * @devices_file: (nullable): LVM devices file or %NULL for default
+ * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @device was successfully added to @devices_file or not
+ *
+ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
+ */
+gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
+    const gchar *args[5] = {"lvmdevices", "--adddev", device, NULL, NULL};
+    g_autofree gchar *devfile = NULL;
+
+    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
+        return FALSE;
+
+    if (!_lvm_devices_enabled ()) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
+                     "LVM devices file not enabled.");
+        return FALSE;
+    }
+
+    if (devices_file) {
+        devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
+        args[3] = devfile;
+    }
+
+    return bd_utils_exec_and_report_error (args, extra, error);
+}
+
+/**
+ * bd_lvm_devices_delete:
+ * @device: device (PV) to delete from the devices file
+ * @devices_file: (nullable): LVM devices file or %NULL for default
+ * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @device was successfully removed from @devices_file or not
+ *
+ * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
+ */
+gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
+    const gchar *args[5] = {"lvmdevices", "--deldev", device, NULL, NULL};
+    g_autofree gchar *devfile = NULL;
+
+    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
+        return FALSE;
+
+    if (!_lvm_devices_enabled ()) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
+                     "LVM devices file not enabled.");
+        return FALSE;
+    }
+
+    if (devices_file) {
+        devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
+        args[3] = devfile;
+    }
+
+    return bd_utils_exec_and_report_error (args, extra, error);
+}
+
+/**
+ * bd_lvm_config_get:
+ * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config
+ * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section
+ * @type: type of the config, e.g. 'full' or 'current'
+ * @values_only: whether to include only values without keys in the output
+ * @global_config: whether to include our internal global config in the call or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): Requested LVM config @section and @setting configuration or %NULL in case of error.
+ *
+ * Tech category: %BD_LVM_TECH_CONFIG no mode (it is ignored)
+ */
+gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error) {
+    g_autofree gchar *conf_spec = NULL;
+    g_autofree gchar *config_arg = NULL;
+    const gchar *args[7] = {"lvmconfig", "--typeconfig", NULL, NULL, NULL, NULL, NULL};
+    guint next_arg = 2;
+    gchar *output = NULL;
+    gboolean success = FALSE;
+
+    if (!section && setting) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Specifying setting without section is not supported.");
+        return NULL;
+    }
+
+    if (section)
+        if (setting)
+            conf_spec = g_strdup_printf ("%s/%s", section, setting);
+        else
+            conf_spec = g_strdup (section);
+    else
+        conf_spec = NULL;
+
+    args[next_arg++] = type;
+    args[next_arg++] = conf_spec;
+    if (values_only)
+        args[next_arg++] = "--valuesonly";
+
+    g_mutex_lock (&global_config_lock);
+    if (global_config && global_config_str) {
+        config_arg = g_strdup_printf ("--config=%s", global_config_str);
+        args[next_arg++] = config_arg;
+    }
+    g_mutex_unlock (&global_config_lock);
+
+    success = bd_utils_exec_and_capture_output (args, extra, &output, error);
+    if (!success)
+        return NULL;
+    return g_strchomp (output);
+}
+
+gboolean _vgcfgbackup_restore (const gchar *command, const gchar *vg_name, const gchar *file, const BDExtraArg **extra, GError **error) {
+    const gchar *args[6] = {"lvm", NULL, NULL, NULL, NULL, NULL};
+    guint next_arg = 1;
+    gchar *output = NULL;
+    g_autofree gchar *config_arg = NULL;
+
+    args[next_arg++] = command;
+    if (file) {
+        args[next_arg++] = "-f";
+        args[next_arg++] = file;
+    }
+    args[next_arg++] = vg_name;
+
+    g_mutex_lock (&global_config_lock);
+    if (global_config_str) {
+        config_arg = g_strdup_printf ("--config=%s", global_config_str);
+        args[next_arg++] = config_arg;
+    }
+    g_mutex_unlock (&global_config_lock);
+
+    return bd_utils_exec_and_capture_output (args, extra, &output, error);
+}
+
+/**
+ * bd_lvm_vgcfgbackup:
+ * @vg_name: name of the VG to backup configuration
+ * @backup_file: (nullable): file to save the backup to or %NULL for using the default backup file
+ *                           in /etc/lvm/backup
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgbackup command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Note: This function does not back up the data content of LVs. See `vgcfbackup(8)` man page
+ *       for more information.
+ *
+ * Returns: Whether the backup was successfully created or not.
+ *
+ * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
+ */
+gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) {
+    return _vgcfgbackup_restore ("vgcfgbackup", vg_name, backup_file, extra, error);
+}
+
+/**
+ * bd_lvm_vgcfgrestore:
+ * @vg_name: name of the VG to restore configuration
+ * @backup_file: (nullable): file to restore VG configuration from to or %NULL for using the
+ *                           latest backup in /etc/lvm/backup
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgcfgrestore command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Note: This function restores VG configuration created by %bd_lvm_vgcfgbackup from given
+ *       @backup_file or from the latest backup in /etc/lvm/backup.
+ *
+ * Returns: Whether the configuration was successfully restored or not.
+ *
+ * Tech category: %BD_LVM_TECH_VG_CFG_BACKUP_RESTORE no mode (it is ignored)
+ */
+gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error) {
+    return _vgcfgbackup_restore ("vgcfgrestore", vg_name, backup_file, extra, error);
+}
diff -pruN 3.3.1-3/src/plugins/lvm/lvm-dbus.c 3.4.0-1/src/plugins/lvm/lvm-dbus.c
--- 3.3.1-3/src/plugins/lvm/lvm-dbus.c	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/lvm-dbus.c	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,4220 @@
+/*
+ * Copyright (C) 2015-2016  Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vratislav Podzimek <vpodzime@redhat.com>
+ */
+
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+#include <unistd.h>
+#include <blockdev/utils.h>
+#include <gio/gio.h>
+#include <libdevmapper.h>
+
+#include "lvm.h"
+#include "lvm-private.h"
+#include "check_deps.h"
+#include "dm_logging.h"
+#include "vdo_stats.h"
+
+#define LVM_BUS_NAME "com.redhat.lvmdbus1"
+#define LVM_OBJ_PREFIX "/com/redhat/lvmdbus1"
+#define MANAGER_OBJ "/com/redhat/lvmdbus1/Manager"
+#define MANAGER_INTF "com.redhat.lvmdbus1.Manager"
+#define JOB_OBJ_PREFIX "/com/redhat/lvmdbus1/Job/"
+#define JOB_INTF "com.redhat.lvmdbus1.Job"
+#define PV_OBJ_PREFIX LVM_OBJ_PREFIX"/Pv"
+#define VG_OBJ_PREFIX LVM_OBJ_PREFIX"/Vg"
+#define LV_OBJ_PREFIX LVM_OBJ_PREFIX"/Lv"
+#define HIDDEN_LV_OBJ_PREFIX LVM_OBJ_PREFIX"/HiddenLv"
+#define THIN_POOL_OBJ_PREFIX LVM_OBJ_PREFIX"/ThinPool"
+#define CACHE_POOL_OBJ_PREFIX LVM_OBJ_PREFIX"/CachePool"
+#define VDO_POOL_OBJ_PREFIX LVM_OBJ_PREFIX"/VdoPool"
+#define PV_INTF LVM_BUS_NAME".Pv"
+#define VG_INTF LVM_BUS_NAME".Vg"
+#define VG_VDO_INTF LVM_BUS_NAME".VgVdo"
+#define LV_CMN_INTF LVM_BUS_NAME".LvCommon"
+#define LV_INTF LVM_BUS_NAME".Lv"
+#define CACHED_LV_INTF LVM_BUS_NAME".CachedLv"
+#define SNAP_INTF LVM_BUS_NAME".Snapshot"
+#define THPOOL_INTF LVM_BUS_NAME".ThinPool"
+#define CACHE_POOL_INTF LVM_BUS_NAME".CachePool"
+#define VDO_POOL_INTF LVM_BUS_NAME".VdoPool"
+#define DBUS_PROPS_IFACE "org.freedesktop.DBus.Properties"
+#define DBUS_INTRO_IFACE "org.freedesktop.DBus.Introspectable"
+#define METHOD_CALL_TIMEOUT 5000
+#define PROGRESS_WAIT 500 * 1000 /* microseconds */
+
+
+static GDBusConnection *bus = NULL;
+
+/**
+ * SECTION: lvm
+ * @short_description: plugin for operations with LVM
+ * @title: LVM
+ * @include: lvm.h
+ *
+ * A plugin for operations with LVM. All sizes passed in/out to/from
+ * the functions are in bytes.
+ */
+
+/**
+ * bd_lvm_error_quark: (skip)
+ */
+GQuark bd_lvm_error_quark (void)
+{
+    return g_quark_from_static_string ("g-bd-lvm-error-quark");
+}
+
+BDLVMPVdata* bd_lvm_pvdata_copy (BDLVMPVdata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMPVdata *new_data = g_new0 (BDLVMPVdata, 1);
+
+    new_data->pv_name = g_strdup (data->pv_name);
+    new_data->pv_uuid = g_strdup (data->pv_uuid);
+    new_data->pv_free = data->pv_free;
+    new_data->pv_size = data->pv_size;
+    new_data->pe_start = data->pe_start;
+    new_data->vg_name = g_strdup (data->vg_name);
+    new_data->vg_uuid = g_strdup (data->vg_uuid);
+    new_data->vg_size = data->vg_size;
+    new_data->vg_free = data->vg_free;
+    new_data->vg_extent_size = data->vg_extent_size;
+    new_data->vg_extent_count = data->vg_extent_count;
+    new_data->vg_free_count = data->vg_free_count;
+    new_data->vg_pv_count = data->vg_pv_count;
+    new_data->pv_tags = g_strdupv (data->pv_tags);
+
+    return new_data;
+}
+
+void bd_lvm_pvdata_free (BDLVMPVdata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data->pv_name);
+    g_free (data->pv_uuid);
+    g_free (data->vg_name);
+    g_free (data->vg_uuid);
+    g_strfreev (data->pv_tags);
+    g_free (data);
+}
+
+BDLVMVGdata* bd_lvm_vgdata_copy (BDLVMVGdata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMVGdata *new_data = g_new0 (BDLVMVGdata, 1);
+
+    new_data->name = g_strdup (data->name);
+    new_data->uuid = g_strdup (data->uuid);
+    new_data->size = data->size;
+    new_data->free = data->free;
+    new_data->extent_size = data->extent_size;
+    new_data->extent_count = data->extent_count;
+    new_data->free_count = data->free_count;
+    new_data->pv_count = data->pv_count;
+    new_data->vg_tags = g_strdupv (data->vg_tags);
+    return new_data;
+}
+
+void bd_lvm_vgdata_free (BDLVMVGdata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data->name);
+    g_free (data->uuid);
+    g_strfreev (data->vg_tags);
+    g_free (data);
+}
+
+BDLVMLVdata* bd_lvm_lvdata_copy (BDLVMLVdata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMLVdata *new_data = g_new0 (BDLVMLVdata, 1);
+
+    new_data->lv_name = g_strdup (data->lv_name);
+    new_data->vg_name = g_strdup (data->vg_name);
+    new_data->uuid = g_strdup (data->uuid);
+    new_data->size = data->size;
+    new_data->attr = g_strdup (data->attr);
+    new_data->segtype = g_strdup (data->segtype);
+    new_data->origin = g_strdup (data->origin);
+    new_data->pool_lv = g_strdup (data->pool_lv);
+    new_data->data_lv = g_strdup (data->data_lv);
+    new_data->metadata_lv = g_strdup (data->metadata_lv);
+    new_data->roles = g_strdup (data->roles);
+    new_data->move_pv = g_strdup (data->move_pv);
+    new_data->data_percent = data->data_percent;
+    new_data->metadata_percent = data->metadata_percent;
+    new_data->copy_percent = data->copy_percent;
+    new_data->lv_tags = g_strdupv (data->lv_tags);
+    return new_data;
+}
+
+void bd_lvm_lvdata_free (BDLVMLVdata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data->lv_name);
+    g_free (data->vg_name);
+    g_free (data->uuid);
+    g_free (data->attr);
+    g_free (data->segtype);
+    g_free (data->origin);
+    g_free (data->pool_lv);
+    g_free (data->data_lv);
+    g_free (data->metadata_lv);
+    g_free (data->roles);
+    g_free (data->move_pv);
+    g_strfreev (data->lv_tags);
+    g_free (data);
+}
+
+BDLVMCacheStats* bd_lvm_cache_stats_copy (BDLVMCacheStats *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMCacheStats *new = g_new0 (BDLVMCacheStats, 1);
+
+    new->block_size = data->block_size;
+    new->cache_size = data->cache_size;
+    new->cache_used = data->cache_used;
+    new->md_block_size = data->md_block_size;
+    new->md_size = data->md_size;
+    new->md_used = data->md_used;
+    new->read_hits = data->read_hits;
+    new->read_misses = data->read_misses;
+    new->write_hits = data->write_hits;
+    new->write_misses = data->write_misses;
+    new->mode = data->mode;
+
+    return new;
+}
+
+void bd_lvm_cache_stats_free (BDLVMCacheStats *data) {
+    g_free (data);
+}
+
+static gboolean setup_dbus_connection (GError **error) {
+    gchar *addr = NULL;
+
+    addr = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+    if (!addr) {
+        g_prefix_error (error, "Failed to get system bus address: ");
+        return FALSE;
+    }
+
+    bus = g_dbus_connection_new_for_address_sync (addr,
+                                                  G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT|
+                                                  G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
+                                                  NULL, NULL, error);
+
+    g_free (addr);
+
+    if (!bus) {
+        g_prefix_error (error, "Failed to create a new connection for the system bus: ");
+        return FALSE;
+    }
+
+    if (g_dbus_connection_is_closed (bus)) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Connection is closed");
+        return FALSE;
+    }
+
+    g_dbus_connection_set_exit_on_close (bus, FALSE);
+
+    return TRUE;
+}
+
+static volatile guint avail_deps = 0;
+static volatile guint avail_dbus_deps = 0;
+static volatile guint avail_features = 0;
+static volatile guint avail_module_deps = 0;
+static GMutex deps_check_lock;
+
+#define DEPS_LVM 0
+#define DEPS_LVM_MASK (1 << DEPS_LVM)
+#define DEPS_LVMDEVICES 1
+#define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES)
+#define DEPS_LVMCONFIG 2
+#define DEPS_LVMCONFIG_MASK (1 << DEPS_LVMCONFIG)
+#define DEPS_LAST 3
+
+static const UtilDep deps[DEPS_LAST] = {
+    {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"},
+    {"lvmdevices", NULL, NULL, NULL},
+    {"lvmconfig", "2.03.17", "--version", "LVM version:\\s+([\\d\\.]+)"},
+};
+
+#define DBUS_DEPS_LVMDBUSD 0
+#define DBUS_DEPS_LVMDBUSD_MASK (1 << DBUS_DEPS_LVMDBUSD)
+#define DBUS_DEPS_LVMDBUSD_WRITECACHE 1
+#define DBUS_DEPS_LVMDBUSD_WRITECACHE_MASK (1 << DBUS_DEPS_LVMDBUSD_WRITECACHE)
+#define DBUS_DEPS_LAST 2
+
+static const DBusDep dbus_deps[DBUS_DEPS_LAST] = {
+    {LVM_BUS_NAME, LVM_OBJ_PREFIX, G_BUS_TYPE_SYSTEM, NULL, NULL, NULL, NULL},
+    {LVM_BUS_NAME, LVM_OBJ_PREFIX, G_BUS_TYPE_SYSTEM, "1.1.0", "Version", MANAGER_INTF, MANAGER_OBJ},
+};
+
+#define FEATURES_VDO 0
+#define FEATURES_VDO_MASK (1 << FEATURES_VDO)
+#define FEATURES_WRITECACHE 0
+#define FEATURES_WRITECACHE_MASK (1 << FEATURES_WRITECACHE)
+#define FEATURES_LAST 2
+
+static const UtilFeatureDep features[FEATURES_LAST] = {
+    {"lvm", "vdo", "segtypes", NULL},
+    {"lvm", "writecache", "segtypes", NULL},
+};
+
+#define MODULE_DEPS_VDO 0
+#define MODULE_DEPS_VDO_MASK (1 << MODULE_DEPS_VDO)
+#define MODULE_DEPS_LAST 1
+
+static const gchar*const module_deps[MODULE_DEPS_LAST] = { "dm-vdo" };
+
+/**
+ * bd_lvm_init:
+ *
+ * Initializes the plugin. **This function is called automatically by the
+ * library's initialization functions.**
+ *
+ */
+gboolean bd_lvm_init (void) {
+    GError *error = NULL;
+
+    /* the check() call should create the DBus connection for us, but let's not
+       completely rely on it */
+    if (G_UNLIKELY (!bus) && !setup_dbus_connection (&error)) {
+        bd_utils_log_format (BD_UTILS_LOG_CRIT, "Failed to setup DBus connection: %s", error->message);
+        g_clear_error (&error);
+        return FALSE;
+    }
+
+    dm_log_with_errno_init ((dm_log_with_errno_fn) redirect_dm_log);
+#ifdef DEBUG
+    dm_log_init_verbose (LOG_DEBUG);
+#else
+    dm_log_init_verbose (LOG_INFO);
+#endif
+
+    return TRUE;
+}
+
+/**
+ * bd_lvm_close:
+ *
+ * Cleans up after the plugin. **This function is called automatically by the
+ * library's functions that unload it.**
+ *
+ */
+void bd_lvm_close (void) {
+    GError *error = NULL;
+
+    /* the check() call should create the DBus connection for us, but let's not
+       completely rely on it */
+    if (!g_dbus_connection_flush_sync (bus, NULL, &error)) {
+        bd_utils_log_format (BD_UTILS_LOG_CRIT, "Failed to flush DBus connection: %s", error->message);
+        g_clear_error (&error);
+    }
+    if (!g_dbus_connection_close_sync (bus, NULL, &error)) {
+        bd_utils_log_format (BD_UTILS_LOG_CRIT, "Failed to close DBus connection: %s", error->message);
+        g_clear_error (&error);
+    }
+
+    dm_log_with_errno_init (NULL);
+    dm_log_init_verbose (0);
+}
+
+/**
+ * bd_lvm_is_tech_avail:
+ * @tech: the queried tech
+ * @mode: a bit mask of queried modes of operation (#BDLVMTechMode) for @tech
+ * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
+ *
+ * Returns: whether the @tech-@mode combination is available -- supported by the
+ *          plugin implementation and having all the runtime dependencies available
+ */
+gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) {
+    switch (tech) {
+    case BD_LVM_TECH_THIN_CALCS:
+        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
+            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
+                         "Only 'query' supported for thin calculations");
+            return FALSE;
+        } else
+            return TRUE;
+    case BD_LVM_TECH_CALCS:
+        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
+            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
+                         "Only 'query' supported for calculations");
+            return FALSE;
+        } else
+            return TRUE;
+    case BD_LVM_TECH_VDO:
+        return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error) &&
+               check_features (&avail_features, FEATURES_VDO_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
+               check_module_deps (&avail_module_deps, MODULE_DEPS_VDO_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error);
+    case BD_LVM_TECH_WRITECACHE:
+        return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK|DBUS_DEPS_LVMDBUSD_WRITECACHE_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error) &&
+               check_features (&avail_features, FEATURES_WRITECACHE_MASK, features, FEATURES_LAST, &deps_check_lock, error);
+    case BD_LVM_TECH_DEVICES:
+        return check_deps (&avail_deps, DEPS_LVMDEVICES_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    case BD_LVM_TECH_CONFIG:
+        return check_deps (&avail_deps, DEPS_LVMCONFIG_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    case BD_LVM_TECH_VG_CFG_BACKUP_RESTORE:
+        return check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    default:
+        /* everything is supported by this implementation of the plugin */
+        return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error);
+    }
+}
+
+static gchar** get_existing_objects (const gchar *obj_prefix, GError **error) {
+    GVariant *intro_v = NULL;
+    gchar *intro_data = NULL;
+    GDBusNodeInfo *info = NULL;
+    gchar **ret = NULL;
+    GDBusNodeInfo **nodes;
+    guint64 n_nodes = 0;
+    guint64 i = 0;
+
+    intro_v = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj_prefix, DBUS_INTRO_IFACE,
+                                           "Introspect", NULL, NULL, G_DBUS_CALL_FLAGS_NONE,
+                                           -1, NULL, error);
+    if (!intro_v)
+        /* no introspection data, something went wrong (error must be set) */
+        return NULL;
+
+    g_variant_get (intro_v, "(s)", &intro_data);
+    g_variant_unref (intro_v);
+    info = g_dbus_node_info_new_for_xml (intro_data, error);
+    g_free (intro_data);
+
+    for (nodes = info->nodes; (*nodes); nodes++)
+        n_nodes++;
+
+    ret = g_new0 (gchar*, n_nodes + 1);
+    for (nodes = info->nodes, i=0; (*nodes); nodes++, i++) {
+        ret[i] = g_strdup_printf ("%s/%s", obj_prefix, ((*nodes)->path));
+    }
+    ret[i] = NULL;
+
+    g_dbus_node_info_unref (info);
+
+    return ret;
+}
+
+
+/**
+ * get_object_path:
+ * @obj_id: get object path for an LVM object (vgname/lvname)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): object path
+ */
+static gchar* get_object_path (const gchar *obj_id, GError **error) {
+    GVariant *args = NULL;
+    GVariant *ret = NULL;
+    gchar *obj_path = NULL;
+
+    args = g_variant_new ("(s)", obj_id);
+    /* consumes (frees) the 'args' parameter */
+    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, MANAGER_OBJ, MANAGER_INTF,
+                                       "LookUpByLvmId", args, NULL, G_DBUS_CALL_FLAGS_NONE,
+                                       -1, NULL, error);
+    if (!ret)
+        /* error is already set */
+        return NULL;
+
+    g_variant_get (ret, "(o)", &obj_path);
+    g_variant_unref (ret);
+
+    if (g_strcmp0 (obj_path, "/") == 0) {
+        /* not a valid path (at least for us) */
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
+                     "The object with LVM ID '%s' doesn't exist", obj_id);
+        g_free (obj_path);
+        return NULL;
+    }
+
+    return obj_path;
+}
+
+/**
+ * get_object_property:
+ * @obj_path: lvmdbusd object path
+ * @iface: interface on @obj_path object
+ * @property: property to get from @obj_path and @iface
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): object path
+ */
+static GVariant* get_object_property (const gchar *obj_path, const gchar *iface, const gchar *property, GError **error) {
+    GVariant *args = NULL;
+    GVariant *ret = NULL;
+    GVariant *real_ret = NULL;
+
+    args = g_variant_new ("(ss)", iface, property);
+
+    /* consumes (frees) the 'args' parameter */
+    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj_path, DBUS_PROPS_IFACE,
+                                       "Get", args, NULL, G_DBUS_CALL_FLAGS_NONE,
+                                       -1, NULL, error);
+    if (!ret) {
+        g_prefix_error (error, "Failed to get %s property of the %s object: ", property, obj_path);
+        return NULL;
+    }
+
+    g_variant_get (ret, "(v)", &real_ret);
+    g_variant_unref (ret);
+
+    return real_ret;
+}
+
+/**
+ * get_lvm_object_property:
+ * @obj_id: LVM object to get the property for (vgname/lvname)
+ * @iface: interface where @property is defined
+ * @property: property to get from @obj_id and @iface
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): property variant
+ */
+static GVariant* get_lvm_object_property (const gchar *obj_id, const gchar *iface, const gchar *property, GError **error) {
+    gchar *obj_path = NULL;
+    GVariant *ret = NULL;
+
+    obj_path = get_object_path (obj_id, error);
+    if (!obj_path)
+        /* error is already set */
+        return NULL;
+    else {
+        ret = get_object_property (obj_path, iface, property, error);
+        g_free (obj_path);
+        return ret;
+    }
+}
+
+static gboolean unbox_params_and_add (GVariant *params, GVariantBuilder *builder) {
+    GVariantIter iter;
+    GVariant *param = NULL;
+    gboolean ret = FALSE;
+
+    if (g_variant_is_of_type (params, G_VARIANT_TYPE_DICTIONARY)) {
+        g_variant_iter_init (&iter, params);
+        while ((param = g_variant_iter_next_value (&iter))) {
+            g_variant_builder_add_value (builder, param);
+            ret = TRUE;
+        }
+        return ret;
+    }
+
+    if (g_variant_is_of_type (params, G_VARIANT_TYPE_VARIANT)) {
+        param = g_variant_get_variant (params);
+        return unbox_params_and_add (param, builder);
+    }
+
+    if (g_variant_is_container (params)) {
+        g_variant_iter_init (&iter, params);
+        while ((param = g_variant_iter_next_value (&iter)))
+            ret = unbox_params_and_add (param, builder);
+        return ret;
+    }
+
+    return FALSE;
+}
+
+/**
+ * call_lvm_method
+ * @obj: lvmdbusd object path
+ * @intf: interface to call @method on
+ * @method: method to call
+ * @params: parameters for @method
+ * @extra_params: extra parameters for @method
+ * @extra_args: extra command line argument to be passed to the LVM command
+ * @task_id: (out): task ID to watch progress of the operation
+ * @progress_id: (out): progress ID to watch progress of the operation
+ * @lock_config: whether to lock %global_config_lock or not (if %FALSE is given, caller is responsible
+ *               for holding the lock for this call)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): return value of @method (variant)
+ */
+static GVariant* call_lvm_method (const gchar *obj, const gchar *intf, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, guint64 *task_id, guint64 *progress_id, gboolean lock_config, GError **error) {
+    GVariant *config = NULL;
+    GVariant *devices = NULL;
+    GVariant *param = NULL;
+    GVariantIter iter;
+    GVariantBuilder builder;
+    GVariantBuilder extra_builder;
+    GVariant *config_extra_params = NULL;
+    GVariant *tmo = NULL;
+    GVariant *all_params = NULL;
+    GVariant *ret = NULL;
+    gchar *params_str = NULL;
+    gchar *log_msg = NULL;
+    gchar *prog_msg = NULL;
+    const BDExtraArg **extra_p = NULL;
+    gboolean added_extra = FALSE;
+
+    if (!check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error))
+        return NULL;
+
+    /* don't allow global config string changes during the run */
+    if (lock_config)
+        g_mutex_lock (&global_config_lock);
+
+    if (global_config_str || global_devices_str || extra_params || extra_args) {
+        if (global_config_str || global_devices_str || extra_args) {
+            /* add the global config to the extra_params */
+            g_variant_builder_init (&extra_builder, G_VARIANT_TYPE_DICTIONARY);
+
+            if (extra_params)
+                added_extra = unbox_params_and_add (extra_params, &extra_builder);
+
+            if (extra_args) {
+                for (extra_p=extra_args; *extra_p; extra_p++) {
+                    g_variant_builder_add (&extra_builder, "{sv}",
+                                           (*extra_p)->opt ? (*extra_p)->opt : "",
+                                           g_variant_new ("s",
+                                                          (*extra_p)->val ? (*extra_p)->val : ""));
+                    added_extra = TRUE;
+                }
+            }
+            if (global_config_str) {
+                config = g_variant_new ("s", global_config_str);
+                g_variant_builder_add (&extra_builder, "{sv}", "--config", config);
+                added_extra = TRUE;
+            }
+            if (global_devices_str) {
+                devices = g_variant_new ("s", global_devices_str);
+                g_variant_builder_add (&extra_builder, "{sv}", "--devices", devices);
+                added_extra = TRUE;
+            }
+
+            if (added_extra)
+                config_extra_params = g_variant_builder_end (&extra_builder);
+            g_variant_builder_clear (&extra_builder);
+        } else
+            /* just use the extra_params */
+            config_extra_params = extra_params;
+    }
+
+    if (!config_extra_params)
+        /* create an empty dictionary with the extra arguments */
+        config_extra_params = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+
+    /* create new GVariant holding the given parameters with the global
+       config and extra_params merged together appended */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+
+    if (params) {
+        /* add parameters */
+        g_variant_iter_init (&iter, params);
+        while ((param = g_variant_iter_next_value (&iter))) {
+            g_variant_builder_add_value (&builder, param);
+            g_variant_unref (param);
+        }
+    }
+
+    /* add the timeout spec (in seconds) */
+    tmo = g_variant_new ("i", 1);
+    g_variant_builder_add_value (&builder, tmo);
+
+    /* add extra parameters including config */
+    g_variant_builder_add_value (&builder, config_extra_params);
+
+    all_params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    params_str = g_variant_print (all_params, FALSE);
+
+    *task_id = bd_utils_get_next_task_id ();
+    log_msg = g_strdup_printf ("Calling the '%s.%s' method on the '%s' object with the following parameters: '%s'",
+                               intf, method, obj, params_str);
+    bd_utils_log_task_status (*task_id, log_msg);
+    g_free (log_msg);
+
+    /* now do the call with all the parameters */
+    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj, intf, method, all_params,
+                                       NULL, G_DBUS_CALL_FLAGS_NONE, METHOD_CALL_TIMEOUT, NULL, error);
+
+    if (lock_config)
+         g_mutex_unlock (&global_config_lock);
+    prog_msg = g_strdup_printf ("Started the '%s.%s' method on the '%s' object with the following parameters: '%s'",
+                               intf, method, obj, params_str);
+    g_free (params_str);
+    *progress_id = bd_utils_report_started (prog_msg);
+    g_free (prog_msg);
+
+    if (!ret) {
+        g_prefix_error (error, "Failed to call the '%s' method on the '%s' object: ", method, obj);
+        return NULL;
+    }
+
+    return ret;
+}
+
+/**
+ * call_lvm_method_sync
+ * @obj: lvmdbusd object path
+ * @intf: interface to call @method on
+ * @method: method to call
+ * @params: parameters for @method
+ * @extra_params: extra parameters for @method
+ * @extra_args: extra command line argument to be passed to the LVM command
+ * @lock_config: whether to lock %global_config_lock or not (if %FALSE is given, caller is responsible
+ *               for holding the lock for this call)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether calling the method was successful or not
+ */
+static gboolean call_lvm_method_sync (const gchar *obj, const gchar *intf, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
+    GVariant *ret = NULL;
+    gchar *obj_path = NULL;
+    g_autofree gchar *task_path = NULL;
+    guint64 log_task_id = 0;
+    guint64 prog_id = 0;
+    gdouble progress = 0.0;
+    gchar *log_msg = NULL;
+    gboolean completed = FALSE;
+    gint64 error_code = 0;
+    gchar *error_msg = NULL;
+    GError *l_error = NULL;
+
+    ret = call_lvm_method (obj, intf, method, params, extra_params, extra_args, &log_task_id, &prog_id, lock_config, &l_error);
+    bd_utils_log_task_status (log_task_id, "Done.");
+    if (!ret) {
+        if (l_error) {
+            log_msg = g_strdup_printf ("Got error: %s", l_error->message);
+            bd_utils_log_task_status (log_task_id, log_msg);
+            bd_utils_report_finished (prog_id, log_msg);
+            g_free (log_msg);
+            g_propagate_error (error, l_error);
+        } else {
+            bd_utils_log_task_status (log_task_id, "Got unknown error");
+            bd_utils_report_finished (prog_id, "Got unknown error");
+        }
+        return FALSE;
+    }
+    if (g_variant_check_format_string (ret, "((oo))", TRUE)) {
+        g_variant_get (ret, "((oo))", &obj_path, &task_path);
+        if (g_strcmp0 (obj_path, "/") != 0) {
+            log_msg = g_strdup_printf ("Got result: %s", obj_path);
+            bd_utils_log_task_status (log_task_id, log_msg);
+            g_free (log_msg);
+            /* got a valid result, just return */
+            g_variant_unref (ret);
+            g_free (obj_path);
+            bd_utils_report_finished (prog_id, "Completed");
+            return TRUE;
+        } else {
+            g_variant_unref (ret);
+            g_free (obj_path);
+            if (g_strcmp0 (task_path, "/") == 0) {
+                log_msg = g_strdup_printf ("Task finished without result and without job started");
+                g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                             "Running '%s' method on the '%s' object failed: %s",
+                             method, obj, log_msg);
+                bd_utils_log_task_status (log_task_id, log_msg);
+                bd_utils_report_finished (prog_id, log_msg);
+                g_free (log_msg);
+                return FALSE;
+            }
+        }
+    } else if (g_variant_check_format_string (ret, "(o)", TRUE)) {
+        g_variant_get (ret, "(o)", &task_path);
+        if (g_strcmp0 (task_path, "/") != 0) {
+            g_variant_unref (ret);
+        } else {
+            bd_utils_log_task_status (log_task_id, "No result, no job started");
+            bd_utils_report_finished (prog_id, "Completed");
+            g_variant_unref (ret);
+            return TRUE;
+        }
+    } else {
+        g_variant_unref (ret);
+        bd_utils_log_task_status (log_task_id, "Failed to parse the returned value!");
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                     "Failed to parse the returned value!");
+        bd_utils_report_finished (prog_id, "Failed to parse the returned value!");
+        return FALSE;
+    }
+
+    log_msg = g_strdup_printf ("Waiting for job '%s' to finish", task_path);
+    bd_utils_log_task_status (log_task_id, log_msg);
+    g_free (log_msg);
+
+    ret = NULL;
+    while (!completed && !l_error) {
+        g_usleep (PROGRESS_WAIT);
+        ret = get_object_property (task_path, JOB_INTF, "Complete", &l_error);
+        if (ret) {
+            g_variant_get (ret, "b", &completed);
+            g_variant_unref (ret);
+            ret = NULL;
+        }
+        if (!completed && !l_error) {
+            /* let's report progress and wait longer */
+            ret = get_object_property (task_path, JOB_INTF, "Percent", &l_error);
+            if (ret) {
+                g_variant_get (ret, "d", &progress);
+                bd_utils_report_progress (prog_id, (gint) progress, NULL);
+                g_variant_unref (ret);
+                ret = NULL;
+            } else {
+                bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Got error when getting progress: %s", l_error->message);
+                g_clear_error (&l_error);
+            }
+            log_msg = g_strdup_printf ("Still waiting for job '%s' to finish", task_path);
+            bd_utils_log_task_status (log_task_id, log_msg);
+            g_free (log_msg);
+        }
+
+    }
+    log_msg = g_strdup_printf ("Job '%s' finished", task_path);
+    bd_utils_log_task_status (log_task_id, log_msg);
+    g_free (log_msg);
+
+    obj_path = NULL;
+    if (!l_error) {
+        ret = get_object_property (task_path, JOB_INTF, "Result", &l_error);
+        if (!ret) {
+            g_prefix_error (&l_error, "Getting result after waiting for '%s' method of the '%s' object failed: ",
+                            method, obj);
+            bd_utils_report_finished (prog_id, l_error->message);
+            g_propagate_error (error, l_error);
+            return FALSE;
+        } else {
+            g_variant_get (ret, "o", &obj_path);
+            g_variant_unref (ret);
+            if (g_strcmp0 (obj_path, "/") != 0) {
+                log_msg = g_strdup_printf ("Got result: %s", obj_path);
+                bd_utils_log_task_status (log_task_id, log_msg);
+                g_free (log_msg);
+            } else {
+                ret = get_object_property (task_path, JOB_INTF, "GetError", &l_error);
+                g_variant_get (ret, "(is)", &error_code, &error_msg);
+                if (error_code != 0) {
+                    if (error_msg) {
+                        log_msg = g_strdup_printf ("Got error: %s", error_msg);
+                        bd_utils_log_task_status (log_task_id, log_msg);
+                        bd_utils_report_finished (prog_id, log_msg);
+                        g_set_error (&l_error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                                     "Running '%s' method on the '%s' object failed: %s",
+                                     method, obj, error_msg);
+                        g_free (log_msg);
+                        g_free (error_msg);
+                    } else {
+                        bd_utils_log_task_status (log_task_id, "Got unknown error");
+                        bd_utils_report_finished (prog_id, "Got unknown error");
+                        g_set_error (&l_error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                                     "Got unknown error when running '%s' method on the '%s' object.",
+                                     method, obj);
+                    }
+                    g_propagate_error (error, l_error);
+                    return FALSE;
+                } else
+                    bd_utils_log_task_status (log_task_id, "No result");
+            }
+            bd_utils_report_finished (prog_id, "Completed");
+            g_free (obj_path);
+
+            /* remove the job object and clean after ourselves */
+            ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, task_path, JOB_INTF, "Remove", NULL,
+                                               NULL, G_DBUS_CALL_FLAGS_NONE, METHOD_CALL_TIMEOUT, NULL, NULL);
+            if (ret)
+                g_variant_unref (ret);
+
+            return TRUE;
+        }
+    } else {
+        /* some real error */
+        g_prefix_error (&l_error, "Waiting for '%s' method of the '%s' object to finish failed: ",
+                        method, obj);
+        g_propagate_error (error, l_error);
+        bd_utils_report_finished (prog_id, "Completed");
+        return FALSE;
+    }
+}
+
+static gboolean call_lvm_obj_method_sync (const gchar *obj_id, const gchar *intf, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
+    g_autofree gchar *obj_path = get_object_path (obj_id, error);
+    if (!obj_path)
+        return FALSE;
+
+    return call_lvm_method_sync (obj_path, intf, method, params, extra_params, extra_args, lock_config, error);
+}
+
+static gboolean call_lv_method_sync (const gchar *vg_name, const gchar *lv_name, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
+    g_autofree gchar *obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    return call_lvm_obj_method_sync (obj_id, LV_INTF, method, params, extra_params, extra_args, lock_config, error);
+}
+
+static gboolean call_thpool_method_sync (const gchar *vg_name, const gchar *pool_name, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
+    g_autofree gchar *obj_id = g_strdup_printf ("%s/%s", vg_name, pool_name);
+
+    return call_lvm_obj_method_sync (obj_id, THPOOL_INTF, method, params, extra_params, extra_args, lock_config, error);
+}
+
+static gboolean call_vdopool_method_sync (const gchar *vg_name, const gchar *pool_name, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
+    g_autofree gchar *obj_id = g_strdup_printf ("%s/%s", vg_name, pool_name);
+
+    return call_lvm_obj_method_sync (obj_id, VDO_POOL_INTF, method, params, extra_params, extra_args, lock_config, error);
+}
+
+static GVariant* get_lv_property (const gchar *vg_name, const gchar *lv_name, const gchar *property, GError **error) {
+    gchar *lv_spec = NULL;
+    GVariant *ret = NULL;
+
+    lv_spec = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    ret = get_lvm_object_property (lv_spec, LV_CMN_INTF, property, error);
+    g_free (lv_spec);
+
+    return ret;
+}
+
+static GVariant* get_object_properties (const gchar *obj_path, const gchar *iface, GError **error) {
+    GVariant *args = NULL;
+    GVariant *ret = NULL;
+    GVariant *real_ret = NULL;
+
+    args = g_variant_new ("(s)", iface);
+
+    /* consumes (frees) the 'args' parameter */
+    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj_path, DBUS_PROPS_IFACE,
+                                       "GetAll", args, NULL, G_DBUS_CALL_FLAGS_NONE,
+                                       -1, NULL, error);
+    if (!ret) {
+        g_prefix_error (error, "Failed to get properties of the %s object: ", obj_path);
+        return NULL;
+    }
+
+    real_ret = g_variant_get_child_value (ret, 0);
+    g_variant_unref (ret);
+
+    return real_ret;
+}
+
+static GVariant* get_lvm_object_properties (const gchar *obj_id, const gchar *iface, GError **error) {
+    GVariant *args = NULL;
+    GVariant *ret = NULL;
+    gchar *obj_path = NULL;
+
+    args = g_variant_new ("(s)", obj_id);
+    /* consumes (frees) the 'args' parameter */
+    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, MANAGER_OBJ, MANAGER_INTF,
+                                       "LookUpByLvmId", args, NULL, G_DBUS_CALL_FLAGS_NONE,
+                                       -1, NULL, error);
+    g_variant_get (ret, "(o)", &obj_path);
+    g_variant_unref (ret);
+
+    if (g_strcmp0 (obj_path, "/") == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
+                     "The object with LVM ID '%s' doesn't exist", obj_id);
+        g_free (obj_path);
+        return NULL;
+    }
+
+    ret = get_object_properties (obj_path, iface, error);
+    g_free (obj_path);
+    return ret;
+}
+
+
+static GVariant* get_pv_properties (const gchar *pv_name, GError **error) {
+    gchar *obj_id = NULL;
+    GVariant *ret = NULL;
+
+    if (!g_str_has_prefix (pv_name, "/dev/")) {
+        obj_id = g_strdup_printf ("/dev/%s", pv_name);
+        ret = get_lvm_object_properties (obj_id, PV_INTF, error);
+        g_free (obj_id);
+    } else
+        ret = get_lvm_object_properties (pv_name, PV_INTF, error);
+
+    return ret;
+}
+
+static GVariant* get_vg_properties (const gchar *vg_name, GError **error) {
+    GVariant *ret = NULL;
+
+    ret = get_lvm_object_properties (vg_name, VG_INTF, error);
+
+    return ret;
+}
+
+static GVariant* get_lv_properties (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    gchar *lvm_spec = NULL;
+    GVariant *ret = NULL;
+
+    lvm_spec = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    ret = get_lvm_object_properties (lvm_spec, LV_CMN_INTF, error);
+    g_free (lvm_spec);
+
+    return ret;
+}
+
+static GVariant* get_vdo_properties (const gchar *vg_name, const gchar *pool_name, GError **error) {
+    gchar *lvm_spec = NULL;
+    GVariant *ret = NULL;
+
+    lvm_spec = g_strdup_printf ("%s/%s", vg_name, pool_name);
+
+    ret = get_lvm_object_properties (lvm_spec, VDO_POOL_INTF, error);
+    g_free (lvm_spec);
+
+    return ret;
+}
+
+static BDLVMPVdata* get_pv_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
+    BDLVMPVdata *data = g_new0 (BDLVMPVdata, 1);
+    GVariantDict dict;
+    gchar *path = NULL;
+    GVariant *vg_props = NULL;
+    GVariant *value = NULL;
+    gsize n_children = 0;
+    gsize i = 0;
+    gchar **tags = NULL;
+    GError *l_error = NULL;
+
+    g_variant_dict_init (&dict, props);
+
+    g_variant_dict_lookup (&dict, "Name", "s", &(data->pv_name));
+    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->pv_uuid));
+    g_variant_dict_lookup (&dict, "FreeBytes", "t", &(data->pv_free));
+    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->pv_size));
+    g_variant_dict_lookup (&dict, "PeStart", "t", &(data->pe_start));
+    g_variant_dict_lookup (&dict, "Missing", "b", &(data->missing));
+
+    value = g_variant_dict_lookup_value (&dict, "Tags", (GVariantType*) "as");
+    if (value) {
+        n_children = g_variant_n_children (value);
+        tags = g_new0 (gchar*, n_children + 1);
+        for (i=0; i < n_children; i++) {
+            g_variant_get_child (value, i, "s", tags+i);
+        }
+        data->pv_tags = tags;
+        g_variant_unref (value);
+    }
+
+    /* returns an object path for the VG */
+    g_variant_dict_lookup (&dict, "Vg", "&o", &path);
+    if (g_strcmp0 (path, "/") == 0) {
+        /* no VG, the PV is not part of any VG */
+        g_variant_dict_clear (&dict);
+        return data;
+    }
+
+    vg_props = get_object_properties (path, VG_INTF, &l_error);
+    g_variant_dict_clear (&dict);
+    if (!vg_props) {
+        if (l_error) {
+            bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Failed to get VG properties for PV %s: %s",
+                                 data->pv_name, l_error->message);
+            g_clear_error (&l_error);
+        }
+        return data;
+    }
+
+    g_variant_dict_init (&dict, vg_props);
+    g_variant_dict_lookup (&dict, "Name", "s", &(data->vg_name));
+    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->vg_uuid));
+    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->vg_size));
+    g_variant_dict_lookup (&dict, "FreeBytes", "t", &(data->vg_free));
+    g_variant_dict_lookup (&dict, "ExtentSizeBytes", "t", &(data->vg_extent_size));
+    g_variant_dict_lookup (&dict, "ExtentCount", "t", &(data->vg_extent_count));
+    g_variant_dict_lookup (&dict, "FreeCount", "t", &(data->vg_free_count));
+    g_variant_dict_lookup (&dict, "PvCount", "t", &(data->vg_pv_count));
+
+    g_variant_dict_clear (&dict);
+    g_variant_unref (vg_props);
+
+    return data;
+}
+
+static BDLVMVGdata* get_vg_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
+    BDLVMVGdata *data = g_new0 (BDLVMVGdata, 1);
+    GVariantDict dict;
+    GVariant *value = NULL;
+    gsize n_children = 0;
+    gsize i = 0;
+    gchar **tags = NULL;
+
+    g_variant_dict_init (&dict, props);
+
+    g_variant_dict_lookup (&dict, "Name", "s", &(data->name));
+    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->uuid));
+
+    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->size));
+    g_variant_dict_lookup (&dict, "FreeBytes", "t", &(data->free));
+    g_variant_dict_lookup (&dict, "ExtentSizeBytes", "t", &(data->extent_size));
+    g_variant_dict_lookup (&dict, "ExtentCount", "t", &(data->extent_count));
+    g_variant_dict_lookup (&dict, "FreeCount", "t", &(data->free_count));
+    g_variant_dict_lookup (&dict, "PvCount", "t", &(data->pv_count));
+    g_variant_dict_lookup (&dict, "Exportable", "b", &(data->exported));
+
+    value = g_variant_dict_lookup_value (&dict, "Tags", (GVariantType*) "as");
+    if (value) {
+        n_children = g_variant_n_children (value);
+        tags = g_new0 (gchar*, n_children + 1);
+        for (i=0; i < n_children; i++) {
+            g_variant_get_child (value, i, "s", tags+i);
+        }
+        data->vg_tags = tags;
+        g_variant_unref (value);
+    }
+
+    g_variant_dict_clear (&dict);
+
+    return data;
+}
+
+static gchar* _lvm_data_lv_name (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *prop = NULL;
+    gchar *obj_id = NULL;
+    gchar *obj_path = NULL;
+    gchar *ret = NULL;
+    gchar *segtype = NULL;
+
+    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    obj_path = get_object_path (obj_id, error);
+    g_free (obj_id);
+    if (!obj_path)
+        return NULL;
+
+    prop = get_lv_property (vg_name, lv_name, "SegType", error);
+    if (!prop)
+        return NULL;
+    g_variant_get_child (prop, 0, "s", &segtype);
+    g_variant_unref (prop);
+
+    if (g_strcmp0 (segtype, "thin-pool") == 0)
+        prop = get_object_property (obj_path, THPOOL_INTF, "DataLv", NULL);
+    else if (g_strcmp0 (segtype, "cache-pool") == 0)
+        prop = get_object_property (obj_path, CACHE_POOL_INTF, "DataLv", NULL);
+    else if (g_strcmp0 (segtype, "vdo-pool") == 0)
+        prop = get_object_property (obj_path, VDO_POOL_INTF, "DataLv", NULL);
+
+    g_free (segtype);
+    g_free (obj_path);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "o", &obj_path);
+    g_variant_unref (prop);
+
+    if (g_strcmp0 (obj_path, "/") == 0) {
+        /* no origin LV */
+        g_free (obj_path);
+        return NULL;
+    }
+    prop = get_object_property (obj_path, LV_CMN_INTF, "Name", error);
+    if (!prop) {
+        g_free (obj_path);
+        return NULL;
+    }
+
+    g_variant_get (prop, "s", &ret);
+    g_variant_unref (prop);
+
+    return g_strstrip (g_strdelimit (ret, "[]", ' '));
+}
+
+static gchar* _lvm_metadata_lv_name (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *prop = NULL;
+    gchar *obj_id = NULL;
+    gchar *obj_path = NULL;
+    gchar *ret = NULL;
+
+    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    obj_path = get_object_path (obj_id, error);
+    g_free (obj_id);
+    if (!obj_path)
+        return NULL;
+
+    prop = get_object_property (obj_path, THPOOL_INTF, "MetaDataLv", NULL);
+    if (!prop)
+        prop = get_object_property (obj_path, CACHE_POOL_INTF, "MetaDataLv", NULL);
+    g_free (obj_path);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "o", &obj_path);
+    g_variant_unref (prop);
+
+    if (g_strcmp0 (obj_path, "/") == 0) {
+        /* no origin LV */
+        g_free (obj_path);
+        return NULL;
+    }
+    prop = get_object_property (obj_path, LV_CMN_INTF, "Name", error);
+    if (!prop) {
+        g_free (obj_path);
+        return NULL;
+    }
+
+    g_variant_get (prop, "s", &ret);
+    g_variant_unref (prop);
+
+    return g_strstrip (g_strdelimit (ret, "[]", ' '));
+}
+
+static BDLVMSEGdata** _lvm_segs (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *prop = NULL;
+    BDLVMSEGdata **segs;
+    gsize n_segs;
+    GVariantIter iter, iter2;
+    GVariant *pv_segs, *pv_name_prop;
+    const gchar *pv;
+    gchar *pv_name;
+    guint64 pv_first_pe, pv_last_pe;
+    int i;
+
+    prop = get_lv_property (vg_name, lv_name, "Devices", error);
+    if (!prop)
+        return NULL;
+
+    /* Count number of segments */
+    n_segs = 0;
+    g_variant_iter_init (&iter, prop);
+    while (g_variant_iter_next (&iter, "(&o@a(tts))", NULL, &pv_segs)) {
+      n_segs += g_variant_n_children (pv_segs);
+      g_variant_unref (pv_segs);
+    }
+
+    if (n_segs == 0) {
+      g_variant_unref (prop);
+      return NULL;
+    }
+
+    /* Build segments */
+    segs = g_new0 (BDLVMSEGdata *, n_segs+1);
+    i = 0;
+    g_variant_iter_init (&iter, prop);
+    while (g_variant_iter_next (&iter, "(&o@a(tts))", &pv, &pv_segs)) {
+      pv_name_prop = get_object_property (pv, PV_INTF, "Name", NULL);
+      if (pv_name_prop) {
+        g_variant_get (pv_name_prop, "&s", &pv_name);
+        g_variant_iter_init (&iter2, pv_segs);
+        while (g_variant_iter_next (&iter2, "(tt&s)", &pv_first_pe, &pv_last_pe, NULL)) {
+          BDLVMSEGdata *seg = g_new0(BDLVMSEGdata, 1);
+          seg->pv_start_pe = pv_first_pe;
+          seg->size_pe = pv_last_pe - pv_first_pe + 1;
+          seg->pvdev = g_strdup (pv_name);
+          segs[i++] = seg;
+        }
+        g_variant_unref (pv_name_prop);
+      }
+      g_variant_unref (pv_segs);
+    }
+
+    g_variant_unref (prop);
+    return segs;
+}
+
+static void _lvm_data_and_metadata_lvs (const gchar *vg_name, const gchar *lv_name,
+                                        gchar ***data_lvs_ret, gchar ***metadata_lvs_ret,
+                                        GError **error) {
+  GVariant *prop;
+  gsize n_hidden_lvs;
+  gchar **data_lvs;
+  gchar **metadata_lvs;
+  GVariantIter iter, iter2;
+  int i_data;
+  int i_metadata;
+  const gchar *sublv;
+  GVariant *sublv_roles_prop;
+  GVariant *sublv_name_prop;
+  gchar *sublv_name;
+  const gchar *role;
+
+  prop = get_lv_property (vg_name, lv_name, "HiddenLvs", error);
+  if (!prop) {
+    *data_lvs_ret = NULL;
+    *metadata_lvs_ret = NULL;
+    return;
+  }
+
+  n_hidden_lvs = g_variant_n_children (prop);
+  data_lvs = g_new0(gchar *, n_hidden_lvs + 1);
+  metadata_lvs = g_new0(gchar *, n_hidden_lvs + 1);
+
+  i_data = 0;
+  i_metadata = 0;
+  g_variant_iter_init (&iter, prop);
+  while (g_variant_iter_next (&iter, "&o", &sublv)) {
+    sublv_roles_prop = get_object_property (sublv, LV_INTF, "Roles", NULL);
+    if (sublv_roles_prop) {
+      sublv_name_prop = get_object_property (sublv, LV_INTF, "Name", NULL);
+      if (sublv_name_prop) {
+        g_variant_get (sublv_name_prop, "s", &sublv_name);
+        if (sublv_name) {
+          sublv_name = g_strstrip (g_strdelimit (sublv_name, "[]", ' '));
+          g_variant_iter_init (&iter2, sublv_roles_prop);
+          while (g_variant_iter_next (&iter2, "&s", &role)) {
+            if (g_strcmp0 (role, "image") == 0) {
+              data_lvs[i_data++] = sublv_name;
+              sublv_name = NULL;
+              break;
+            } else if (g_strcmp0 (role, "metadata") == 0) {
+              metadata_lvs[i_metadata++] = sublv_name;
+              sublv_name = NULL;
+              break;
+            }
+          }
+          g_free (sublv_name);
+        }
+        g_variant_unref (sublv_name_prop);
+      }
+      g_variant_unref (sublv_roles_prop);
+    }
+  }
+
+  *data_lvs_ret = data_lvs;
+  *metadata_lvs_ret = metadata_lvs;
+
+  g_variant_unref (prop);
+  return;
+}
+
+static BDLVMLVdata* get_lv_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
+    BDLVMLVdata *data = g_new0 (BDLVMLVdata, 1);
+    GVariantDict dict;
+    GVariant *value = NULL;
+    gchar *path = NULL;
+    GVariant *name = NULL;
+    gsize n_children = 0;
+    gsize i = 0;
+    gchar **roles = NULL;
+    gchar **tags = NULL;
+
+    g_variant_dict_init (&dict, props);
+
+    g_variant_dict_lookup (&dict, "Name", "s", &(data->lv_name));
+    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->uuid));
+    g_variant_dict_lookup (&dict, "Attr", "s", &(data->attr));
+    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->size));
+    g_variant_dict_lookup (&dict, "DataPercent", "u", &(data->data_percent));
+    g_variant_dict_lookup (&dict, "MetaDataPercent", "u", &(data->metadata_percent));
+    g_variant_dict_lookup (&dict, "CopyPercent", "u", &(data->copy_percent));
+
+    /* XXX: how to deal with LVs with multiple segment types? We are just taking
+            the first one now. */
+    value = g_variant_dict_lookup_value (&dict, "SegType", (GVariantType*) "as");
+    if (value) {
+        const gchar *st;
+        g_variant_get_child (value, 0, "&s", &st);
+        if (g_strcmp0 (st, "error") == 0)
+          st = "linear";
+        data->segtype = g_strdup (st);
+        g_variant_unref (value);
+    }
+
+    value = g_variant_dict_lookup_value (&dict, "Roles", (GVariantType*) "as");
+    if (value) {
+        n_children = g_variant_n_children (value);
+        roles = g_new0 (gchar*, n_children + 1);
+        for (i=0; i < n_children; i++)
+            g_variant_get_child (value, i, "&s", roles+i);
+        data->roles = g_strjoinv (",", roles);
+        g_free (roles);
+        g_variant_unref (value);
+    }
+
+    /* returns an object path for the VG */
+    g_variant_dict_lookup (&dict, "Vg", "o", &path);
+    name = get_object_property (path, VG_INTF, "Name", NULL);
+    g_free (path);
+    g_variant_get (name, "s", &(data->vg_name));
+    g_variant_unref (name);
+
+    g_variant_dict_lookup (&dict, "OriginLv", "o", &path);
+    if (g_strcmp0 (path, "/") != 0) {
+        name = get_object_property (path, LV_CMN_INTF, "Name", NULL);
+        g_variant_get (name, "s", &(data->origin));
+        g_variant_unref (name);
+    }
+    g_free (path);
+    path = NULL;
+
+    g_variant_dict_lookup (&dict, "PoolLv", "o", &path);
+    if (g_strcmp0 (path, "/") != 0) {
+        name = get_object_property (path, LV_CMN_INTF, "Name", NULL);
+        g_variant_get (name, "s", &(data->pool_lv));
+        g_variant_unref (name);
+    }
+    g_free (path);
+    path = NULL;
+
+    g_variant_dict_lookup (&dict, "MovePv", "o", &path);
+    if (path && g_strcmp0 (path, "/") != 0) {
+        name = get_object_property (path, PV_INTF, "Name", NULL);
+        g_variant_get (name, "s", &(data->move_pv));
+        g_variant_unref (name);
+    }
+    g_free (path);
+    path = NULL;
+
+    value = g_variant_dict_lookup_value (&dict, "Tags", (GVariantType*) "as");
+    if (value) {
+        n_children = g_variant_n_children (value);
+        tags = g_new0 (gchar*, n_children + 1);
+        for (i=0; i < n_children; i++) {
+            g_variant_get_child (value, i, "s", tags+i);
+        }
+        data->lv_tags = tags;
+        g_variant_unref (value);
+    }
+
+    g_variant_dict_clear (&dict);
+    g_variant_unref (props);
+
+    return data;
+}
+
+static BDLVMVDOPooldata* get_vdo_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
+    BDLVMVDOPooldata *data = g_new0 (BDLVMVDOPooldata, 1);
+    GVariantDict dict;
+    gchar *value = NULL;
+
+    g_variant_dict_init (&dict, props);
+
+    g_variant_dict_lookup (&dict, "OperatingMode", "s", &value);
+    if (g_strcmp0 (value, "recovering") == 0)
+        data->operating_mode = BD_LVM_VDO_MODE_RECOVERING;
+    else if (g_strcmp0 (value, "read-only") == 0)
+        data->operating_mode = BD_LVM_VDO_MODE_READ_ONLY;
+    else if (g_strcmp0 (value, "normal") == 0)
+        data->operating_mode = BD_LVM_VDO_MODE_NORMAL;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO operating mode: %s", value);
+        data->operating_mode = BD_LVM_VDO_MODE_UNKNOWN;
+    }
+    g_free (value);
+    value = NULL;
+
+    g_variant_dict_lookup (&dict, "CompressionState", "s", &value);
+    if (g_strcmp0 (value, "online") == 0)
+        data->compression_state = BD_LVM_VDO_COMPRESSION_ONLINE;
+    else if (g_strcmp0 (value, "offline") == 0)
+        data->compression_state = BD_LVM_VDO_COMPRESSION_OFFLINE;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO compression state: %s", value);
+        data->compression_state = BD_LVM_VDO_COMPRESSION_UNKNOWN;
+    }
+    g_free (value);
+    value = NULL;
+
+    g_variant_dict_lookup (&dict, "IndexState", "s", &value);
+    if (g_strcmp0 (value, "error") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_ERROR;
+    else if (g_strcmp0 (value, "closed") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_CLOSED;
+    else if (g_strcmp0 (value, "opening") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_OPENING;
+    else if (g_strcmp0 (value, "closing") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_CLOSING;
+    else if (g_strcmp0 (value, "offline") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_OFFLINE;
+    else if (g_strcmp0 (value, "online") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_ONLINE;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO index state: %s", value);
+        data->index_state = BD_LVM_VDO_INDEX_UNKNOWN;
+    }
+    g_free (value);
+    value = NULL;
+
+    g_variant_dict_lookup (&dict, "WritePolicy", "s", &value);
+    if (g_strcmp0 (value, "auto") == 0)
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_AUTO;
+    else if (g_strcmp0 (value, "sync") == 0)
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_SYNC;
+    else if (g_strcmp0 (value, "async") == 0)
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_ASYNC;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO write policy: %s", value);
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
+    }
+    g_free (value);
+    value = NULL;
+
+    g_variant_dict_lookup (&dict, "UsedSize", "t", &(data->used_size));
+    g_variant_dict_lookup (&dict, "SavingPercent", "d", &(data->saving_percent));
+
+    g_variant_dict_lookup (&dict, "IndexMemorySize", "t", &(data->index_memory_size));
+
+    g_variant_dict_lookup (&dict, "Compression", "s", &value);
+    if (value && g_strcmp0 (value, "enabled") == 0)
+        data->compression = TRUE;
+    else
+        data->compression = FALSE;
+    g_free (value);
+    value = NULL;
+
+    g_variant_dict_lookup (&dict, "Deduplication", "s", &value);
+    if (value && g_strcmp0 (value, "enabled") == 0)
+        data->deduplication = TRUE;
+    else
+        data->deduplication = FALSE;
+    g_free (value);
+    value = NULL;
+
+    g_variant_dict_clear (&dict);
+    g_variant_unref (props);
+
+    return data;
+}
+
+static GVariant* create_size_str_param (guint64 size, const gchar *unit) {
+    gchar *str = NULL;
+
+    str = g_strdup_printf ("%"G_GUINT64_FORMAT"%s", size, unit ? unit : "");
+    return g_variant_new_take_string (str);
+}
+
+/**
+ * bd_lvm_pvcreate:
+ * @device: the device to make PV from
+ * @data_alignment: data (first PE) alignment or 0 to use the default
+ * @metadata_size: size of the area reserved for metadata or 0 to use the default
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the PV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_pvcreate (const gchar *device, guint64 data_alignment, guint64 metadata_size, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *param = NULL;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+
+    if (data_alignment != 0 || metadata_size != 0) {
+        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+        if (data_alignment != 0) {
+            param = create_size_str_param (data_alignment, "b");
+            g_variant_builder_add (&builder, "{sv}", "dataalignment", param);
+        }
+
+        if (metadata_size != 0) {
+            param = create_size_str_param (metadata_size, "b");
+            g_variant_builder_add (&builder, "{sv}", "metadatasize", param);
+        }
+        extra_params = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+    }
+
+    params = g_variant_new ("(s)", device);
+
+    return call_lvm_method_sync (MANAGER_OBJ, MANAGER_INTF, "PvCreate", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_pvresize:
+ * @device: the device to resize
+ * @size: the new requested size of the PV or 0 if it should be adjusted to device's size
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the PV's size was successfully changed or not
+ *
+ * If given @size different from 0, sets the PV's size to the given value (see
+ * pvresize(8)). If given @size 0, adjusts the PV's size to the underlying
+ * block device's size.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg **extra, GError **error) {
+    GVariant *params = NULL;
+    gchar *obj_path = get_object_path (device, error);
+    if (!obj_path)
+        return FALSE;
+
+    params = g_variant_new ("(t)", size);
+    return call_lvm_method_sync (obj_path, PV_INTF, "ReSize", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_pvremove:
+ * @device: the PV device to be removed/destroyed
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV removal
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the PV was successfully removed/destroyed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
+ */
+gboolean bd_lvm_pvremove (const gchar *device, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    GError *l_error = NULL;
+    gboolean ret = FALSE;
+
+    if (access (device, F_OK) != 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
+                     "The device '%s' doesn't exist", device);
+        return FALSE;
+    }
+
+    /* one has to be really persuasive to remove a PV (the double --force is not
+       bug, at least not in this code) */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+    g_variant_builder_add (&builder, "{sv}", "-ff", g_variant_new ("s", ""));
+    g_variant_builder_add (&builder, "{sv}", "--yes", g_variant_new ("s", ""));
+
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+    ret = call_lvm_obj_method_sync (device, PV_INTF, "Remove", NULL, params, extra, TRUE, &l_error);
+    if (!ret && l_error && g_error_matches (l_error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST)) {
+        /* if the object doesn't exist, the given device is not a PV and thus
+           this function should be a noop */
+        g_clear_error (&l_error);
+        ret = TRUE;
+    }
+
+    if (l_error)
+        g_propagate_error (error, l_error);
+    return ret;
+}
+
+/**
+ * bd_lvm_pvmove:
+ * @src: the PV device to move extents off of
+ * @dest: (nullable): the PV device to move extents onto or %NULL
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV move
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the extents from the @src PV where successfully moved or not
+ *
+ * If @dest is %NULL, VG allocation rules are used for the extents from the @src
+ * PV (see pvmove(8)).
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_pvmove (const gchar *src, const gchar *dest, const BDExtraArg **extra, GError **error) {
+    GVariant *prop = NULL;
+    gchar *src_path = NULL;
+    gchar *dest_path = NULL;
+    gchar *vg_obj_path = NULL;
+    GVariantBuilder builder;
+    GVariantType *type = NULL;
+    GVariant *dest_var = NULL;
+    GVariant *params = NULL;
+    GError *l_error = NULL;
+    gboolean ret = FALSE;
+
+    src_path = get_object_path (src, &l_error);
+    if (!src_path || (g_strcmp0 (src_path, "/") == 0)) {
+        if (!l_error)
+            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
+                         "The source PV '%s' doesn't exist", src);
+        else
+            g_propagate_error (error, l_error);
+        return FALSE;
+    }
+    if (dest) {
+        dest_path = get_object_path (dest, &l_error);
+        if (!dest_path || (g_strcmp0 (dest_path, "/") == 0)) {
+            if (!l_error)
+                g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
+                             "The destination PV '%s' doesn't exist", dest);
+            else
+                g_propagate_error (error, l_error);
+            return FALSE;
+        }
+    }
+    prop = get_object_property (src_path, PV_INTF, "Vg", error);
+    if (!prop) {
+        g_free (src_path);
+        return FALSE;
+    }
+    g_variant_get (prop, "o", &vg_obj_path);
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("o", src_path));
+    g_variant_builder_add_value (&builder, g_variant_new ("(tt)", (guint64) 0, (guint64) 0));
+    if (dest) {
+        dest_var = g_variant_new ("(ott)", dest_path, (guint64) 0, (guint64) 0);
+        g_variant_builder_add_value (&builder, g_variant_new_array (NULL, &dest_var, 1));
+    } else {
+        type = g_variant_type_new ("a(ott)");
+        g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
+        g_variant_type_free (type);
+    }
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    ret = call_lvm_method_sync (vg_obj_path, VG_INTF, "Move", params, NULL, extra, TRUE, error);
+
+    g_free (src_path);
+    g_free (dest_path);
+    g_free (vg_obj_path);
+    return ret;
+}
+
+/**
+ * bd_lvm_pvscan:
+ * @device: (nullable): the device to scan for PVs or %NULL
+ * @update_cache: whether to update the lvmetad cache or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV scan
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the system or @device was successfully scanned for PVs or not
+ *
+ * The @device argument is used only if @update_cache is %TRUE. Otherwise the
+ * whole system is scanned for PVs.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_pvscan (const gchar *device, gboolean update_cache, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariantType *type = NULL;
+    GVariant *params = NULL;
+    GVariant *device_var = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    /* update the cache and specify the device (if any) */
+    g_variant_builder_add_value (&builder, g_variant_new_boolean (FALSE));
+    g_variant_builder_add_value (&builder, g_variant_new_boolean (update_cache));
+    if (update_cache && device) {
+        device_var = g_variant_new ("s", device);
+        g_variant_builder_add_value (&builder, g_variant_new_array (NULL, &device_var, 1));
+    } else {
+        type = g_variant_type_new ("as");
+        g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
+        g_variant_type_free (type);
+    }
+    /* (major, minor)`s, we never specify them */
+    type = g_variant_type_new ("a(ii)");
+    g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
+    g_variant_type_free (type);
+
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_lvm_method_sync (MANAGER_OBJ, MANAGER_INTF, "PvScan", params, NULL, extra, TRUE, error);
+}
+
+
+static gboolean _manage_lvm_tags (const gchar *objpath, const gchar *pv_path, const gchar *intf, const gchar **tags, const gchar *func, GError **error) {
+    guint num_tags = g_strv_length ((gchar **) tags);
+    GVariant *params = NULL;
+    GVariant **tags_array = NULL;
+    GVariantBuilder builder;
+    GVariant *pv_var = NULL;
+    gboolean ret = FALSE;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+
+    if (pv_path) {
+        /* PV tags are set from the VG interface so we need to add the PV as an argument here */
+        pv_var = g_variant_new ("o", pv_path);
+        g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_OBJECT_PATH, &pv_var, 1));
+    }
+
+    tags_array = g_new0 (GVariant *, num_tags + 1);
+    for (guint i = 0; i < num_tags; i++)
+        tags_array[i] = g_variant_new_string (tags[i]);
+
+    g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_STRING, tags_array, num_tags));
+
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    ret = call_lvm_method_sync (objpath, intf, func, params, NULL, NULL, TRUE, error);
+    g_free (tags_array);
+    return ret;
+}
+
+/**
+ * bd_lvm_add_pv_tags:
+ * @device: the device to set PV tags for
+ * @tags: (array zero-terminated=1): list of tags to add
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully added to @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_add_pv_tags (const gchar *device, const gchar **tags, GError **error) {
+    BDLVMPVdata *pvinfo = NULL;
+    g_autofree gchar *vg_path = NULL;
+    g_autofree gchar *pv_path = NULL;
+
+    pv_path = get_object_path (device, error);
+    if (!pv_path)
+        return FALSE;
+
+    pvinfo = bd_lvm_pvinfo (device, error);
+    if (!pvinfo)
+        return FALSE;
+
+    if (!pvinfo->vg_name) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Tags can't be added to PVs without a VG");
+        bd_lvm_pvdata_free (pvinfo);
+        return FALSE;
+    }
+
+    vg_path = get_object_path (pvinfo->vg_name, error);
+    bd_lvm_pvdata_free (pvinfo);
+    if (!vg_path)
+        return FALSE;
+
+    return _manage_lvm_tags (vg_path, pv_path, VG_INTF, tags, "PvTagsAdd", error);
+}
+
+/**
+ * bd_lvm_delete_pv_tags:
+ * @device: the device to set PV tags for
+ * @tags: (array zero-terminated=1): list of tags to remove
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully removed from @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_delete_pv_tags (const gchar *device, const gchar **tags, GError **error)  {
+    BDLVMPVdata *pvinfo = NULL;
+    g_autofree gchar *vg_path = NULL;
+    g_autofree gchar *pv_path = NULL;
+
+    pv_path = get_object_path (device, error);
+    if (!pv_path)
+        return FALSE;
+
+    pvinfo = bd_lvm_pvinfo (device, error);
+    if (!pvinfo)
+        return FALSE;
+
+    if (!pvinfo->vg_name) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
+                     "Tags can't be removed from PVs without a VG");
+        bd_lvm_pvdata_free (pvinfo);
+        return FALSE;
+    }
+
+    vg_path = get_object_path (pvinfo->vg_name, error);
+    bd_lvm_pvdata_free (pvinfo);
+    if (!vg_path)
+        return FALSE;
+
+    return _manage_lvm_tags (vg_path, pv_path, VG_INTF, tags, "PvTagsDel", error);
+}
+/**
+ * bd_lvm_pvinfo:
+ * @device: a PV to get information about or %NULL
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the PV on the given @device or
+ * %NULL in case of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMPVdata* bd_lvm_pvinfo (const gchar *device, GError **error) {
+    GVariant *props = NULL;
+    BDLVMPVdata *ret = NULL;
+
+    props = get_pv_properties (device, error);
+    if (!props)
+        /* the error is already populated */
+        return NULL;
+
+    ret = get_pv_data_from_props (props, error);
+    g_variant_unref (props);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_pvs:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about PVs found in the system
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMPVdata** bd_lvm_pvs (GError **error) {
+    gchar **objects = NULL;
+    guint64 n_pvs = 0;
+    GVariant *props = NULL;
+    BDLVMPVdata **ret = NULL;
+    guint64 i = 0;
+    GError *l_error = NULL;
+
+    objects = get_existing_objects (PV_OBJ_PREFIX, &l_error);
+    if (!objects) {
+        if (!l_error) {
+            /* no PVs */
+            ret = g_new0 (BDLVMPVdata*, 1);
+            ret[0] = NULL;
+            return ret;
+        } else {
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+    }
+
+    n_pvs = g_strv_length ((gchar **) objects);
+
+    /* now create the return value -- NULL-terminated array of BDLVMPVdata */
+    ret = g_new0 (BDLVMPVdata*, n_pvs + 1);
+    for (i=0; i < n_pvs; i++) {
+        props = get_object_properties (objects[i], PV_INTF, error);
+        if (!props) {
+            g_strfreev (objects);
+            g_free (ret);
+            return NULL;
+        }
+        ret[i] = get_pv_data_from_props (props, error);
+        g_variant_unref (props);
+        if (!(ret[i])) {
+            g_strfreev (objects);
+            g_free (ret);
+            return NULL;
+        }
+    }
+    ret[i] = NULL;
+
+    g_strfreev (objects);
+    return ret;
+}
+
+/**
+ * bd_lvm_vgcreate:
+ * @name: name of the newly created VG
+ * @pv_list: (array zero-terminated=1): list of PVs the newly created VG should use
+ * @pe_size: PE size or 0 if the default value should be used
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG @name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_vgcreate (const gchar *name, const gchar **pv_list, guint64 pe_size, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    gchar *path = NULL;
+    const gchar **pv = NULL;
+    GVariant *pvs = NULL;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+
+    /* build the array of PVs (object paths) */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
+    for (pv=pv_list; *pv; pv++) {
+        path = get_object_path (*pv, error);
+        if (!path) {
+            g_variant_builder_clear (&builder);
+            return FALSE;
+        }
+        g_variant_builder_add_value (&builder, g_variant_new ("o", path));
+    }
+    pvs = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    /* build the params tuple */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("s", name));
+    g_variant_builder_add_value (&builder, pvs);
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    /* pe_size needs to go to extra_params params */
+    pe_size = RESOLVE_PE_SIZE (pe_size);
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+    g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--physicalextentsize", create_size_str_param (pe_size, "b")));
+    extra_params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_lvm_method_sync (MANAGER_OBJ, MANAGER_INTF, "VgCreate", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgremove:
+ * @vg_name: name of the to be removed VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG removal
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully removed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
+ */
+gboolean bd_lvm_vgremove (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Remove", NULL, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgrename:
+ * @old_vg_name: old name of the VG to rename
+ * @new_vg_name: new name for the @old_vg_name VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG rename
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully renamed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgrename (const gchar *old_vg_name, const gchar *new_vg_name, const BDExtraArg **extra, GError **error) {
+    GVariant *params = g_variant_new ("(s)", new_vg_name);
+    return call_lvm_obj_method_sync (old_vg_name, VG_INTF, "Rename", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgactivate:
+ * @vg_name: name of the to be activated VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG activation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully activated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    GVariant *params = g_variant_new ("(t)", (guint64) 0);
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Activate", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgdeactivate:
+ * @vg_name: name of the to be deactivated VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG deactivation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully deactivated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    GVariant *params = g_variant_new ("(t)", (guint64) 0);
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Deactivate", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgextend:
+ * @vg_name: name of the to be extended VG
+ * @device: PV device to extend the @vg_name VG with
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG extension
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG @vg_name was successfully extended with the given @device or not.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
+    g_autofree gchar *pv = NULL;
+    GVariant *pv_var = NULL;
+    GVariant *pvs = NULL;
+    GVariant *params = NULL;
+
+    pv = get_object_path (device, error);
+    if (!pv)
+        return FALSE;
+
+    pv_var = g_variant_new ("o", pv);
+    pvs = g_variant_new_array (NULL, &pv_var, 1);
+    params = g_variant_new_tuple (&pvs, 1);
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Extend", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgreduce:
+ * @vg_name: name of the to be reduced VG
+ * @device: (nullable): PV device the @vg_name VG should be reduced of or %NULL
+ *                        if the VG should be reduced of the missing PVs
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG reduction
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG @vg_name was successfully reduced of the given @device or not
+ *
+ * Note: This function does not move extents off of the PV before removing
+ *       it from the VG. You must do that first by calling #bd_lvm_pvmove.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
+    g_autofree gchar *pv = NULL;
+    GVariantBuilder builder;
+    GVariantType *type = NULL;
+    GVariant *pv_var = NULL;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+
+    if (device) {
+        pv = get_object_path (device, error);
+        if (!pv)
+            return FALSE;
+    }
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    if (device) {
+        /* do not remove missing */
+        pv_var = g_variant_new ("o", pv);
+        g_variant_builder_add_value (&builder, g_variant_new_boolean (FALSE));
+        g_variant_builder_add_value (&builder, g_variant_new_array (NULL, &pv_var, 1));
+        params = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+    } else {
+        /* remove missing */
+        g_variant_builder_add_value (&builder, g_variant_new_boolean (TRUE));
+        type = g_variant_type_new ("ao");
+        g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
+        g_variant_type_free (type);
+        params = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+
+        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+        g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--force", g_variant_new ("s", "")));
+        extra_params = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+    }
+
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Reduce", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_add_vg_tags:
+ * @vg_name: the VG to set tags on
+ * @tags: (array zero-terminated=1): list of tags to add
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully added to @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
+    g_autofree gchar *obj_path = get_object_path (vg_name, error);
+    if (!obj_path)
+        return FALSE;
+
+    return _manage_lvm_tags (obj_path, NULL, VG_INTF, tags, "TagsAdd", error);
+}
+
+/**
+ * bd_lvm_delete_vg_tags:
+ * @vg_name: the VG to set tags on
+ * @tags: (array zero-terminated=1): list of tags to remove
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully removed from @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
+    g_autofree gchar *obj_path = get_object_path (vg_name, error);
+    if (!obj_path)
+        return FALSE;
+
+    return _manage_lvm_tags (obj_path, NULL, VG_INTF, tags, "TagsDel", error);
+}
+
+static gboolean _vglock_start_stop (const gchar *vg_name, gboolean start, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+    if (start)
+        g_variant_builder_add (&builder, "{sv}", "--lockstart", g_variant_new ("s", ""));
+    else
+        g_variant_builder_add (&builder, "{sv}", "--lockstop", g_variant_new ("s", ""));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Change", NULL, params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vglock_start:
+ * @vg_name: a shared VG to start the lockspace in lvmlockd
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the lock was successfully started for @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    return _vglock_start_stop (vg_name, TRUE, extra, error);
+}
+
+/**
+ * bd_lvm_vglock_stop:
+ * @vg_name: a shared VG to stop the lockspace in lvmlockd
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the lock was successfully stopped for @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    return _vglock_start_stop (vg_name, FALSE, extra, error);
+}
+
+/**
+ * bd_lvm_vginfo:
+ * @vg_name: a VG to get information about
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the @vg_name VG or %NULL in case
+ * of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error) {
+    GVariant *props = NULL;
+    BDLVMVGdata *ret = NULL;
+
+    props = get_vg_properties (vg_name, error);
+    if (!props)
+        /* the error is already populated */
+        return NULL;
+
+    ret = get_vg_data_from_props (props, error);
+    g_variant_unref (props);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_vgs:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about VGs found in the system
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMVGdata** bd_lvm_vgs (GError **error) {
+    gchar **objects = NULL;
+    guint64 n_vgs = 0;
+    GVariant *props = NULL;
+    BDLVMVGdata **ret = NULL;
+    guint64 i = 0;
+    GError *l_error = NULL;
+
+    objects = get_existing_objects (VG_OBJ_PREFIX, &l_error);
+    if (!objects) {
+        if (!l_error) {
+            /* no VGs */
+            ret = g_new0 (BDLVMVGdata*, 1);
+            ret[0] = NULL;
+            return ret;
+        } else {
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+    }
+
+    n_vgs = g_strv_length ((gchar **) objects);
+
+    /* now create the return value -- NULL-terminated array of BDLVMVGdata */
+    ret = g_new0 (BDLVMVGdata*, n_vgs + 1);
+    for (i=0; i < n_vgs; i++) {
+        props = get_object_properties (objects[i], VG_INTF, error);
+        if (!props) {
+            g_strfreev (objects);
+            g_free (ret);
+            return NULL;
+        }
+        ret[i] = get_vg_data_from_props (props, error);
+        g_variant_unref (props);
+        if (!(ret[i])) {
+            g_strfreev (objects);
+            g_free (ret);
+            return NULL;
+        }
+    }
+    ret[i] = NULL;
+
+    g_strfreev (objects);
+    return ret;
+}
+
+/**
+ * bd_lvm_lvorigin:
+ * @vg_name: name of the VG containing the queried LV
+ * @lv_name: name of the queried LV
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): the origin volume for the @vg_name/@lv_name LV or
+ * %NULL if failed to determine (@error) is set in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_lvorigin (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *prop = NULL;
+    gchar *obj_path = NULL;
+    gchar *ret = NULL;
+
+    prop = get_lv_property (vg_name, lv_name, "OriginLv", error);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "o", &obj_path);
+    g_variant_unref (prop);
+
+    if (g_strcmp0 (obj_path, "/") == 0) {
+        /* no origin LV */
+        g_free (obj_path);
+        return NULL;
+    }
+    prop = get_object_property (obj_path, LV_CMN_INTF, "Name", error);
+    if (!prop) {
+        g_free (obj_path);
+        return NULL;
+    }
+
+    g_variant_get (prop, "s", &ret);
+    g_variant_unref (prop);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_lvcreate:
+ * @vg_name: name of the VG to create a new LV in
+ * @lv_name: name of the to-be-created LV
+ * @size: requested size of the new LV
+ * @type: (nullable): type of the new LV ("striped", "raid1",..., see lvcreate (8))
+ * @pv_list: (nullable) (array zero-terminated=1): list of PVs the newly created LV should use or %NULL
+ * if not specified
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the given @vg_name/@lv_name LV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_lvcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, const gchar *type, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    gchar *path = NULL;
+    const gchar **pv = NULL;
+    GVariant *pvs = NULL;
+    GVariantType *var_type = NULL;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+
+    /* build the array of PVs (object paths) */
+    if (pv_list && *pv_list) {
+        g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+        for (pv=pv_list; *pv; pv++) {
+            path = get_object_path (*pv, error);
+            if (!path) {
+                g_variant_builder_clear (&builder);
+                return FALSE;
+            }
+            g_variant_builder_add_value (&builder, g_variant_new ("(ott)", path, (guint64) 0, (guint64) 0));
+        }
+        pvs = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+    } else {
+        var_type = g_variant_type_new ("a(ott)");
+        pvs = g_variant_new_array (var_type, NULL, 0);
+        g_variant_type_free (var_type);
+    }
+
+    /* build the params tuple */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
+    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
+    g_variant_builder_add_value (&builder, pvs);
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    if (type) {
+        /* and now the extra_params params */
+        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+        if (pv_list && g_strcmp0 (type, "striped") == 0)
+            g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "stripes", g_variant_new ("i", g_strv_length ((gchar **) pv_list))));
+        else
+            g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "type", g_variant_new ("s", type)));
+        extra_params = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+    }
+
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "LvCreate", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_lvremove:
+ * @vg_name: name of the VG containing the to-be-removed LV
+ * @lv_name: name of the to-be-removed LV
+ * @force: whether to force removal or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV removal
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully removed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
+ */
+gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean force, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *extra_params = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+    /* '--yes' is needed if DISCARD is enabled */
+    g_variant_builder_add (&builder, "{sv}", "--yes", g_variant_new ("s", ""));
+    if (force) {
+        g_variant_builder_add (&builder, "{sv}", "--force", g_variant_new ("s", ""));
+    }
+    extra_params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_lv_method_sync (vg_name, lv_name, "Remove", NULL, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_lvrename:
+ * @vg_name: name of the VG containing the to-be-renamed LV
+ * @lv_name: name of the to-be-renamed LV
+ * @new_name: new name for the @vg_name/@lv_name LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV rename
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully renamed to
+ * @vg_name/@new_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error) {
+    GVariant *params = NULL;
+
+    params = g_variant_new ("(s)", new_name);
+    return call_lv_method_sync (vg_name, lv_name, "Rename", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_lvresize:
+ * @vg_name: name of the VG containing the to-be-resized LV
+ * @lv_name: name of the to-be-resized LV
+ * @size: the requested new size of the LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully resized or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariantType *type = NULL;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+    gboolean success = FALSE;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
+    type = g_variant_type_new ("a(ott)");
+    g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
+    g_variant_type_free (type);
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    /* Starting with 2.03.19 we need to add an extra option to avoid
+       any filesystem related checks by lvresize.
+    */
+    success = bd_utils_check_util_version (deps[DEPS_LVM].name, LVM_VERSION_FSRESIZE,
+                                           deps[DEPS_LVM].ver_arg, deps[DEPS_LVM].ver_regexp, NULL);
+    if (success) {
+      g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+      g_variant_builder_add (&builder, "{sv}", "--fs", g_variant_new ("s", "ignore"));
+      extra_params = g_variant_builder_end (&builder);
+      g_variant_builder_clear (&builder);
+    }
+
+    return call_lv_method_sync (vg_name, lv_name, "Resize", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_lvrepair:
+ * @vg_name: name of the VG containing the to-be-repaired LV
+ * @lv_name: name of the to-be-repaired LV
+ * @pv_list: (array zero-terminated=1): list of PVs to be used for the repair
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV repair
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully repaired or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvrepair (const gchar *vg_name, const gchar *lv_name, const gchar **pv_list,
+                          const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    gchar *path = NULL;
+    const gchar **pv = NULL;
+    GVariant *pvs = NULL;
+
+    /* build the array of PVs (object paths) */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
+    for (pv=pv_list; *pv; pv++) {
+        path = get_object_path (*pv, error);
+        if (!path) {
+            g_variant_builder_clear (&builder);
+            return FALSE;
+        }
+        g_variant_builder_add_value (&builder, g_variant_new ("o", path));
+    }
+    pvs = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, pvs);
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_lv_method_sync (vg_name, lv_name, "RepairRaidLv", params, NULL, extra, TRUE, error);
+
+  return FALSE;
+}
+
+/**
+ * bd_lvm_lvactivate:
+ * @vg_name: name of the VG containing the to-be-activated LV
+ * @lv_name: name of the to-be-activated LV
+ * @ignore_skip: whether to ignore the skip flag or not
+ * @shared: whether to activate the LV in shared mode (used for shared LVM setups with lvmlockd,
+ *          use %FALSE if not sure)
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV activation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully activated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) {
+    GVariant *params = NULL;
+    GVariantBuilder builder;
+    GVariant *extra_params = NULL;
+
+    if (shared)
+        params = g_variant_new ("(t)", (guint64) 1 << 6);
+    else
+        params = g_variant_new ("(t)", (guint64) 0);
+
+    if (ignore_skip) {
+        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+        g_variant_builder_add (&builder, "{sv}", "-K", g_variant_new ("s", ""));
+        extra_params = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+    }
+
+    return call_lv_method_sync (vg_name, lv_name, "Activate", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_lvdeactivate:
+ * @vg_name: name of the VG containing the to-be-deactivated LV
+ * @lv_name: name of the to-be-deactivated LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV deactivation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully deactivated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error) {
+    GVariant *params = g_variant_new ("(t)", (guint64) 0);
+    return call_lv_method_sync (vg_name, lv_name, "Deactivate", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_lvsnapshotcreate:
+ * @vg_name: name of the VG containing the LV a new snapshot should be created of
+ * @origin_name: name of the LV a new snapshot should be created of
+ * @snapshot_name: name of the to-be-created snapshot
+ * @size: requested size for the snapshot
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name LV
+ * was successfully created or not.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("s", snapshot_name));
+    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_lv_method_sync (vg_name, origin_name, "Snapshot", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_lvsnapshotmerge:
+ * @vg_name: name of the VG containing the to-be-merged LV snapshot
+ * @snapshot_name: name of the to-be-merged LV snapshot
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot merge
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@snapshot_name LV snapshot was successfully merged or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error) {
+    gchar *obj_id = NULL;
+    gchar *obj_path = NULL;
+
+    /* get object path for vg_name/snapshot_name and call SNAP_INTF, "Merge" */
+    obj_id = g_strdup_printf ("%s/%s", vg_name, snapshot_name);
+    obj_path = get_object_path (obj_id, error);
+    g_free (obj_id);
+    if (!obj_path)
+        return FALSE;
+
+    return call_lvm_method_sync (obj_path, SNAP_INTF, "Merge", NULL, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_add_lv_tags:
+ * @vg_name: name of the VG that contains the LV to set tags on
+ * @lv_name: name of the LV to set tags on
+ * @tags: (array zero-terminated=1): list of tags to add
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully added to @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_add_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
+    g_autofree gchar *obj_id = NULL;
+    g_autofree gchar *obj_path = NULL;
+
+    /* get object path for vg_name/lv_name */
+    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    obj_path = get_object_path (obj_id, error);
+    if (!obj_path)
+        return FALSE;
+
+    return _manage_lvm_tags (obj_path, NULL, LV_INTF, tags, "TagsAdd", error);
+}
+
+/**
+ * bd_lvm_delete_lv_tags:
+ * @vg_name: name of the VG that contains the LV to set tags on
+ * @lv_name: name of the LV to set tags on
+ * @tags: (array zero-terminated=1): list of tags to remove
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully removed from @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_delete_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
+    g_autofree gchar *obj_id = NULL;
+    g_autofree gchar *obj_path = NULL;
+
+    /* get object path for vg_name/lv_name */
+    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    obj_path = get_object_path (obj_id, error);
+    if (!obj_path)
+        return FALSE;
+
+    return _manage_lvm_tags (obj_path, NULL, LV_INTF, tags, "TagsDel", error);
+}
+
+/**
+ * bd_lvm_lvinfo:
+ * @vg_name: name of the VG that contains the LV to get information about
+ * @lv_name: name of the LV to get information about
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
+ * of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMLVdata* bd_lvm_lvinfo (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *props = NULL;
+    BDLVMLVdata* ret = NULL;
+
+    props = get_lv_properties (vg_name, lv_name, error);
+    if (!props)
+        /* the error is already populated */
+        return NULL;
+
+    ret = get_lv_data_from_props (props, error);
+    if (!ret)
+        return NULL;
+
+    if (g_strcmp0 (ret->segtype, "thin-pool") == 0 ||
+        g_strcmp0 (ret->segtype, "cache-pool") == 0) {
+        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
+        ret->metadata_lv = _lvm_metadata_lv_name (vg_name, lv_name, NULL);
+    }
+    if (g_strcmp0 (ret->segtype, "vdo-pool") == 0) {
+        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
+    }
+
+    return ret;
+}
+
+BDLVMLVdata* bd_lvm_lvinfo_tree (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *props = NULL;
+    BDLVMLVdata* ret = NULL;
+
+    props = get_lv_properties (vg_name, lv_name, error);
+    if (!props)
+        /* the error is already populated */
+        return NULL;
+
+    ret = get_lv_data_from_props (props, error);
+    if (!ret)
+        return NULL;
+
+    if (g_strcmp0 (ret->segtype, "thin-pool") == 0 ||
+        g_strcmp0 (ret->segtype, "cache-pool") == 0) {
+        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
+        ret->metadata_lv = _lvm_metadata_lv_name (vg_name, lv_name, NULL);
+    }
+    if (g_strcmp0 (ret->segtype, "vdo-pool") == 0) {
+        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
+    }
+    ret->segs = _lvm_segs (vg_name, lv_name, NULL);
+    _lvm_data_and_metadata_lvs (vg_name, lv_name, &ret->data_lvs, &ret->metadata_lvs, NULL);
+
+    return ret;
+}
+
+static gchar* get_lv_vg_name (const gchar *lv_obj_path, GError **error) {
+    GVariant *value = NULL;
+    gchar *vg_obj_path = NULL;
+    gchar *ret = NULL;
+
+    value = get_object_property (lv_obj_path, LV_CMN_INTF, "Vg", error);
+    g_variant_get (value, "o", &vg_obj_path);
+    g_variant_unref (value);
+
+    value = get_object_property (vg_obj_path, VG_INTF, "Name", error);
+    g_variant_get (value, "s", &ret);
+    g_free (vg_obj_path);
+    g_variant_unref (value);
+
+    return ret;
+}
+
+/**
+ * filter_lvs_by_vg: (skip)
+ *
+ * Filter LVs by VG name and prepend the matching ones to the @out list.
+ */
+static gboolean filter_lvs_by_vg (gchar **lvs, const gchar *vg_name, GSList **out, guint64 *n_lvs, GError **error) {
+    gchar **lv_p = NULL;
+    gchar *lv_vg_name = NULL;
+    gboolean success = TRUE;
+
+    if (!lvs)
+        /* nothing to do */
+        return TRUE;
+
+    for (lv_p=lvs; *lv_p; lv_p++) {
+        if (vg_name) {
+            lv_vg_name = get_lv_vg_name (*lv_p, error);
+            if (!lv_vg_name) {
+                g_free (*lv_p);
+                success = FALSE;
+                continue;
+            }
+
+            if (g_strcmp0 (lv_vg_name, vg_name) == 0) {
+                *out = g_slist_prepend (*out, *lv_p);
+                (*n_lvs)++;
+            } else {
+                g_free (*lv_p);
+            }
+
+            g_free (lv_vg_name);
+        } else {
+            *out = g_slist_prepend (*out, *lv_p);
+            (*n_lvs)++;
+        }
+    }
+    return success;
+}
+
+/**
+ * bd_lvm_lvs:
+ * @vg_name: (nullable): name of the VG to get information about LVs from
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about LVs found in the given
+ * @vg_name VG or in system if @vg_name is %NULL
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMLVdata** bd_lvm_lvs (const gchar *vg_name, GError **error) {
+    gchar **lvs = NULL;
+    guint64 n_lvs = 0;
+    GVariant *props = NULL;
+    BDLVMLVdata **ret = NULL;
+    guint64 j = 0;
+    GSList *matched_lvs = NULL;
+    GSList *lv = NULL;
+    gboolean success = FALSE;
+    GError *l_error = NULL;
+
+    lvs = get_existing_objects (LV_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (THIN_POOL_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (CACHE_POOL_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (VDO_POOL_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (HIDDEN_LV_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    if (n_lvs == 0) {
+        /* no LVs */
+        ret = g_new0 (BDLVMLVdata*, 1);
+        ret[0] = NULL;
+        g_slist_free_full (matched_lvs, g_free);
+        return ret;
+    }
+
+    /* we have been prepending to the list so far, but it will be nicer if we
+       reverse it (to get back the original order) */
+    matched_lvs = g_slist_reverse (matched_lvs);
+
+    /* now create the return value -- NULL-terminated array of BDLVMLVdata */
+    ret = g_new0 (BDLVMLVdata*, n_lvs + 1);
+
+    lv = matched_lvs;
+    while (lv) {
+        props = get_object_properties (lv->data, LV_CMN_INTF, &l_error);
+        if (!props) {
+            g_slist_free_full (matched_lvs, g_free);
+            g_free (ret);
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+        ret[j] = get_lv_data_from_props (props, &l_error);
+        if (!(ret[j])) {
+            g_slist_free_full (matched_lvs, g_free);
+            for (guint64 i = 0; i < j; i++)
+                bd_lvm_lvdata_free (ret[i]);
+            g_free (ret);
+            g_propagate_error (error, l_error);
+            return NULL;
+        } else if ((g_strcmp0 (ret[j]->segtype, "thin-pool") == 0) ||
+                   (g_strcmp0 (ret[j]->segtype, "cache-pool") == 0)) {
+            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
+            ret[j]->metadata_lv = _lvm_metadata_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
+        } else if (g_strcmp0 (ret[j]->segtype, "vdo-pool") == 0) {
+            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
+        }
+        if (l_error) {
+            g_slist_free_full (matched_lvs, g_free);
+            for (guint64 i = 0; i <= j; i++)
+                bd_lvm_lvdata_free (ret[i]);
+            g_free (ret);
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+        j++;
+        lv = g_slist_next (lv);
+    }
+    g_slist_free_full (matched_lvs, g_free);
+
+    ret[j] = NULL;
+    return ret;
+}
+
+BDLVMLVdata** bd_lvm_lvs_tree (const gchar *vg_name, GError **error) {
+    gchar **lvs = NULL;
+    guint64 n_lvs = 0;
+    GVariant *props = NULL;
+    BDLVMLVdata **ret = NULL;
+    guint64 j = 0;
+    GSList *matched_lvs = NULL;
+    GSList *lv = NULL;
+    gboolean success = FALSE;
+    GError *l_error = NULL;
+
+    lvs = get_existing_objects (LV_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (THIN_POOL_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (CACHE_POOL_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (VDO_POOL_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    lvs = get_existing_objects (HIDDEN_LV_OBJ_PREFIX, &l_error);
+    if (!lvs && l_error) {
+        g_propagate_error (error, l_error);
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
+    g_free (lvs);
+    if (!success) {
+        g_slist_free_full (matched_lvs, g_free);
+        return NULL;
+    }
+
+    if (n_lvs == 0) {
+        /* no LVs */
+        ret = g_new0 (BDLVMLVdata*, 1);
+        ret[0] = NULL;
+        g_slist_free_full (matched_lvs, g_free);
+        return ret;
+    }
+
+    /* we have been prepending to the list so far, but it will be nicer if we
+       reverse it (to get back the original order) */
+    matched_lvs = g_slist_reverse (matched_lvs);
+
+    /* now create the return value -- NULL-terminated array of BDLVMLVdata */
+    ret = g_new0 (BDLVMLVdata*, n_lvs + 1);
+
+    lv = matched_lvs;
+    while (lv) {
+        props = get_object_properties (lv->data, LV_CMN_INTF, &l_error);
+        if (!props) {
+            g_slist_free_full (matched_lvs, g_free);
+            g_free (ret);
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+        ret[j] = get_lv_data_from_props (props, &l_error);
+        if (!(ret[j])) {
+            g_slist_free_full (matched_lvs, g_free);
+            for (guint64 i = 0; i < j; i++)
+                bd_lvm_lvdata_free (ret[i]);
+            g_free (ret);
+            g_propagate_error (error, l_error);
+            return NULL;
+        } else if ((g_strcmp0 (ret[j]->segtype, "thin-pool") == 0) ||
+                   (g_strcmp0 (ret[j]->segtype, "cache-pool") == 0)) {
+            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
+            ret[j]->metadata_lv = _lvm_metadata_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
+        } else if (g_strcmp0 (ret[j]->segtype, "vdo-pool") == 0) {
+            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
+        }
+        ret[j]->segs = _lvm_segs (ret[j]->vg_name, ret[j]->lv_name, &l_error);
+        _lvm_data_and_metadata_lvs (ret[j]->vg_name, ret[j]->lv_name, &ret[j]->data_lvs, &ret[j]->metadata_lvs,
+                                    &l_error);
+        if (l_error) {
+            g_slist_free_full (matched_lvs, g_free);
+            for (guint64 i = 0; i <= j; i++)
+                bd_lvm_lvdata_free (ret[i]);
+            g_free (ret);
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+        j++;
+        lv = g_slist_next (lv);
+    }
+    g_slist_free_full (matched_lvs, g_free);
+
+    ret[j] = NULL;
+    return ret;
+}
+
+/**
+ * bd_lvm_thpoolcreate:
+ * @vg_name: name of the VG to create a thin pool in
+ * @lv_name: name of the to-be-created pool LV
+ * @size: requested size of the to-be-created pool
+ * @md_size: requested metadata size or 0 to use the default
+ * @chunk_size: requested chunk size or 0 to use the default
+ * @profile: (nullable): profile to use (see lvm(8) for more information) or %NULL to use
+ *                         the default
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name thin pool was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thpoolcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, guint64 md_size, guint64 chunk_size, const gchar *profile, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+    GVariant *param = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
+    g_variant_builder_add_value (&builder, g_variant_new_uint64 (size));
+    g_variant_builder_add_value (&builder, g_variant_new_boolean (TRUE));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+    if (md_size != 0) {
+        param = create_size_str_param (md_size, "b");
+        g_variant_builder_add (&builder, "{sv}", "poolmetadatasize", param);
+    }
+    if (chunk_size != 0) {
+        param = create_size_str_param (chunk_size, "b");
+        g_variant_builder_add (&builder, "{sv}", "chunksize", param);
+    }
+    if (profile) {
+        g_variant_builder_add (&builder, "{sv}", "profile", g_variant_new ("s", profile));
+    }
+    extra_params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_lvm_obj_method_sync (vg_name, VG_INTF, "LvCreateLinear", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_thlvcreate:
+ * @vg_name: name of the VG containing the thin pool providing extents for the to-be-created thin LV
+ * @pool_name: name of the pool LV providing extents for the to-be-created thin LV
+ * @lv_name: name of the to-be-created thin LV
+ * @size: requested virtual size of the to-be-created thin LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name thin LV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thlvcreate (const gchar *vg_name, const gchar *pool_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
+    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    return call_thpool_method_sync (vg_name, pool_name, "LvCreate", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_thlvpoolname:
+ * @vg_name: name of the VG containing the queried thin LV
+ * @lv_name: name of the queried thin LV
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
+ * thin LV or %NULL if failed to determine (@error) is set in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_thlvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *prop = NULL;
+    gboolean is_thin = FALSE;
+    gchar *pool_obj_path = NULL;
+    gchar *ret = NULL;
+
+    prop = get_lv_property (vg_name, lv_name, "IsThinVolume", error);
+    if (!prop)
+        return NULL;
+    is_thin = g_variant_get_boolean (prop);
+    g_variant_unref (prop);
+
+    if (!is_thin) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
+                     "The LV '%s' is not a thin LV and thus have no thin pool", lv_name);
+        return NULL;
+    }
+    prop = get_lv_property (vg_name, lv_name, "PoolLv", error);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "o", &pool_obj_path);
+    g_variant_unref (prop);
+
+    prop = get_object_property (pool_obj_path, LV_CMN_INTF, "Name", error);
+    g_free (pool_obj_path);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "s", &ret);
+    g_variant_unref (prop);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_thsnapshotcreate:
+ * @vg_name: name of the VG containing the thin LV a new snapshot should be created of
+ * @origin_name: name of the thin LV a new snapshot should be created of
+ * @snapshot_name: name of the to-be-created snapshot
+ * @pool_name: (nullable): name of the thin pool to create the snapshot in or %NULL if not specified
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV snapshot creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name
+ * thin LV was successfully created or not.
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("s", snapshot_name));
+    g_variant_builder_add_value (&builder, g_variant_new ("t", (guint64) 0));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    if (pool_name) {
+        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+        g_variant_builder_add (&builder, "{sv}", "thinpool", g_variant_new ("s", pool_name));
+        extra_params = g_variant_builder_end (&builder);
+        g_variant_builder_clear (&builder);
+    }
+
+    return call_lv_method_sync (vg_name, origin_name, "Snapshot", params, extra_params, extra, TRUE, error);
+}
+
+/**
+ * get_lv_type_from_flags: (skip)
+ * @meta: getting type for a (future) metadata LV
+ *
+ * Get LV type string from flags.
+ */
+static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error G_GNUC_UNUSED) {
+    if (!meta) {
+        if (flags & BD_LVM_CACHE_POOL_STRIPED)
+            return "striped";
+        else if (flags & BD_LVM_CACHE_POOL_RAID1)
+            return "raid1";
+        else if (flags & BD_LVM_CACHE_POOL_RAID5)
+            return "raid5";
+        else if (flags & BD_LVM_CACHE_POOL_RAID6)
+            return "raid6";
+        else if (flags & BD_LVM_CACHE_POOL_RAID10)
+            return "raid10";
+        else
+            return NULL;
+    } else {
+        if (flags & BD_LVM_CACHE_POOL_META_STRIPED)
+            return "striped";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID1)
+            return "raid1";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID5)
+            return "raid5";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID6)
+            return "raid6";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID10)
+            return "raid10";
+        else
+            return NULL;
+    }
+}
+
+/**
+ * bd_lvm_cache_create_pool:
+ * @vg_name: name of the VG to create @pool_name in
+ * @pool_name: name of the cache pool LV to create
+ * @pool_size: desired size of the cache pool @pool_name
+ * @md_size: desired size of the @pool_name cache pool's metadata LV or 0 to
+ *           use the default
+ * @mode: cache mode of the @pool_name cache pool
+ * @flags: a combination of (ORed) #BDLVMCachePoolFlags
+ * @fast_pvs: (array zero-terminated=1): list of (fast) PVs to create the @pool_name
+ *                                       cache pool (and the metadata LV)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cache pool @vg_name/@pool_name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_cache_create_pool (const gchar *vg_name, const gchar *pool_name, guint64 pool_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags, const gchar **fast_pvs, GError **error) {
+    gboolean success = FALSE;
+    const gchar *type = NULL;
+    gchar *name = NULL;
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    GVariant *extra = NULL;
+    gchar *lv_id = NULL;
+    gchar *lv_obj_path = NULL;
+    const gchar *mode_str = NULL;
+    gchar *msg = NULL;
+    guint64 progress_id = 0;
+    GError *l_error = NULL;
+
+    msg = g_strdup_printf ("Started 'create cache pool %s/%s'", vg_name, pool_name);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    /* create an LV for the pool */
+    type = get_lv_type_from_flags (flags, FALSE, NULL);
+    success = bd_lvm_lvcreate (vg_name, pool_name, pool_size, type, fast_pvs, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the pool LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 1/3 steps done */
+    bd_utils_report_progress (progress_id, 33, "Created the data LV");
+
+    /* determine the size of the metadata LV */
+    type = get_lv_type_from_flags (flags, TRUE, NULL);
+    if (md_size == 0)
+        md_size = bd_lvm_cache_get_default_md_size (pool_size, NULL);
+    name = g_strdup_printf ("%s_meta", pool_name);
+
+    /* create the metadata LV */
+    success = bd_lvm_lvcreate (vg_name, name, md_size, type, fast_pvs, NULL, &l_error);
+    if (!success) {
+        g_free (name);
+        g_prefix_error (&l_error, "Failed to create the pool metadata LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 2/3 steps done */
+    bd_utils_report_progress (progress_id, 66, "Created the metadata LV");
+
+    /* create the cache pool from the two LVs */
+    /* build the params tuple */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    lv_id = g_strdup_printf ("%s/%s", vg_name, name);
+    lv_obj_path = get_object_path (lv_id, &l_error);
+    g_free (lv_id);
+    if (!lv_obj_path) {
+        g_variant_builder_clear (&builder);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
+    lv_id = g_strdup_printf ("%s/%s", vg_name, pool_name);
+    lv_obj_path = get_object_path (lv_id, &l_error);
+    g_free (lv_id);
+    if (!lv_obj_path) {
+        g_variant_builder_clear (&builder);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    /* build the dictionary with the extra params */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+    mode_str = bd_lvm_cache_get_mode_str (mode, &l_error);
+    if (!mode_str) {
+        g_variant_builder_clear (&builder);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+    g_variant_builder_add (&builder, "{sv}", "cachemode", g_variant_new ("s", mode_str));
+    extra = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    success = call_lvm_obj_method_sync (vg_name, VG_INTF, "CreateCachePool", params, extra, NULL, TRUE, &l_error);
+    if (!success) {
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+    } else
+        bd_utils_report_finished (progress_id, "Completed");
+
+    return success;
+}
+
+/**
+ * bd_lvm_cache_attach:
+ * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
+ * @data_lv: data LV to attach the @cache_pool_lv to
+ * @cache_pool_lv: cache pool LV to attach to the @data_lv
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @cache_pool_lv was successfully attached to the @data_lv or not
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_cache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_pool_lv, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    gchar *lv_id = NULL;
+    g_autofree gchar *lv_obj_path = NULL;
+    gboolean ret = FALSE;
+
+    lv_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
+    lv_obj_path = get_object_path (lv_id, error);
+    g_free (lv_id);
+    if (!lv_obj_path)
+        return FALSE;
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    lv_id = g_strdup_printf ("%s/%s", vg_name, cache_pool_lv);
+
+    ret = call_lvm_obj_method_sync (lv_id, CACHE_POOL_INTF, "CacheLv", params, NULL, extra, TRUE, error);
+    g_free (lv_id);
+    return ret;
+}
+
+/**
+ * bd_lvm_cache_detach:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: name of the cached LV to detach its cache from
+ * @destroy: whether to destroy the cache after detach or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cache was successfully detached from the @cached_lv or not
+ *
+ * Note: synces the cache first
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_cache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
+    g_autofree gchar *lv_id = NULL;
+    g_autofree gchar *cache_pool_name = NULL;
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("b", destroy));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    cache_pool_name = bd_lvm_cache_pool_name (vg_name, cached_lv, error);
+    if (!cache_pool_name)
+        return FALSE;
+    lv_id = g_strdup_printf ("%s/%s", vg_name, cached_lv);
+    return call_lvm_obj_method_sync (lv_id, CACHED_LV_INTF, "DetachCachePool", params, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_cache_create_cached_lv:
+ * @vg_name: name of the VG to create a cached LV in
+ * @lv_name: name of the cached LV to create
+ * @data_size: size of the data LV
+ * @cache_size: size of the cache (or cached LV more precisely)
+ * @md_size: size of the cache metadata LV or 0 to use the default
+ * @mode: cache mode for the cached LV
+ * @flags: a combination of (ORed) #BDLVMCachePoolFlags
+ * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
+ * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cached LV @lv_name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_cache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags,
+                                        const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
+    gboolean success = FALSE;
+    gchar *name = NULL;
+    gchar *msg = NULL;
+    guint64 progress_id = 0;
+    GError *l_error = NULL;
+
+    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the data LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 1/5 steps (cache pool creation has 3 steps) done */
+    bd_utils_report_progress (progress_id, 20, "Data LV created");
+
+    name = g_strdup_printf ("%s_cache", lv_name);
+    success = bd_lvm_cache_create_pool (vg_name, name, cache_size, md_size, mode, flags, fast_pvs, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the cache pool '%s': ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 4/5 steps (cache pool creation has 3 steps) done */
+    bd_utils_report_progress (progress_id, 80, "Cache pool created");
+
+    success = bd_lvm_cache_attach (vg_name, lv_name, name, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to attach the cache pool '%s' to the data LV: ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    bd_utils_report_finished (progress_id, "Completed");
+    g_free (name);
+    return TRUE;
+}
+
+/**
+ * bd_lvm_writecache_attach:
+ * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
+ * @data_lv: data LV to attach the @cache_lv to
+ * @cache_lv: cache (fast) LV to attach to the @data_lv
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @cache_lv was successfully attached to the @data_lv or not
+ *
+ * Note: Both @data_lv and @cache_lv will be deactivated before the operation.
+ *
+ * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_writecache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_lv, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    gchar *lv_id = NULL;
+    g_autofree gchar *lv_obj_path = NULL;
+    gboolean success = FALSE;
+
+    /* both LVs need to be inactive for the writecache convert to work */
+    success = bd_lvm_lvdeactivate (vg_name, data_lv, NULL, error);
+    if (!success)
+        return FALSE;
+
+    success = bd_lvm_lvdeactivate (vg_name, cache_lv, NULL, error);
+    if (!success)
+        return FALSE;
+
+    lv_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
+    lv_obj_path = get_object_path (lv_id, error);
+    g_free (lv_id);
+    if (!lv_obj_path)
+        return FALSE;
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    lv_id = g_strdup_printf ("%s/%s", vg_name, cache_lv);
+
+    success = call_lvm_obj_method_sync (lv_id, LV_INTF, "WriteCacheLv", params, NULL, extra, TRUE, error);
+    g_free (lv_id);
+    return success;
+}
+
+/**
+ * bd_lvm_writecache_detach:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: name of the cached LV to detach its cache from
+ * @destroy: whether to destroy the cache after detach or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cache was successfully detached from the @cached_lv or not
+ *
+ * Note: synces the cache first
+ *
+ * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_writecache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
+    return bd_lvm_cache_detach (vg_name, cached_lv, destroy, extra, error);
+}
+
+/**
+ * bd_lvm_writecache_create_cached_lv:
+ * @vg_name: name of the VG to create a cached LV in
+ * @lv_name: name of the cached LV to create
+ * @data_size: size of the data LV
+ * @cache_size: size of the cache (or cached LV more precisely)
+ * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
+ * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cached LV @lv_name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_writecache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size,
+                                             const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
+    gboolean success = FALSE;
+    gchar *name = NULL;
+    gchar *msg = NULL;
+    guint64 progress_id = 0;
+    GError *l_error = NULL;
+
+    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    name = g_strdup_printf ("%s_writecache", lv_name);
+    success = bd_lvm_lvcreate (vg_name, name, cache_size, NULL, fast_pvs, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the cache LV '%s': ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 1/3 steps done */
+    bd_utils_report_progress (progress_id, 33, "Cache LV created");
+
+    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the data LV: ");
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 2/3 steps done */
+    bd_utils_report_progress (progress_id, 66, "Data LV created");
+
+    success = bd_lvm_writecache_attach (vg_name, lv_name, name, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to attach the cache LV '%s' to the data LV: ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    bd_utils_report_finished (progress_id, "Completed");
+    g_free (name);
+    return TRUE;
+}
+
+/**
+ * bd_lvm_cache_pool_name:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: cached LV to get the name of the its pool LV for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: name of the cache pool LV used by the @cached_lv or %NULL in case of error
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_cache_pool_name (const gchar *vg_name, const gchar *cached_lv, GError **error) {
+    gchar *ret = NULL;
+    gchar *name_start = NULL;
+    gchar *name_end = NULL;
+    gchar *pool_name = NULL;
+    gchar *lv_spec = NULL;
+    GVariant *prop = NULL;
+    gchar *pool_obj_path = NULL;
+
+    /* same as for a thin LV, but with square brackets */
+    lv_spec = g_strdup_printf ("%s/%s", vg_name, cached_lv);
+    prop = get_lvm_object_property (lv_spec, CACHED_LV_INTF, "CachePool", error);
+    g_free (lv_spec);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "o", &pool_obj_path);
+    prop = get_object_property (pool_obj_path, LV_CMN_INTF, "Name", error);
+    g_free (pool_obj_path);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "s", &ret);
+    g_variant_unref (prop);
+
+    name_start = strchr (ret, '[');
+    if (!name_start) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Failed to determine cache pool name from: '%s'", ret);
+        g_free (ret);
+        return NULL;
+    }
+    name_start++;
+
+    name_end = strchr (ret, ']');
+    if (!name_end) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Failed to determine cache pool name from: '%s'", ret);
+        g_free (ret);
+        return NULL;
+    }
+
+    pool_name = g_strndup (name_start, name_end - name_start);
+    g_free (ret);
+
+    return pool_name;
+}
+
+/**
+ * bd_lvm_cache_stats:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: cached LV to get stats for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: stats for the @cached_lv or %NULL in case of error
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error) {
+    struct dm_pool *pool = NULL;
+    struct dm_task *task = NULL;
+    struct dm_info info;
+    struct dm_status_cache *status = NULL;
+    gchar *map_name = NULL;
+    guint64 start = 0;
+    guint64 length = 0;
+    gchar *type = NULL;
+    gchar *params = NULL;
+    BDLVMCacheStats *ret = NULL;
+    BDLVMLVdata *lvdata = NULL;
+
+    if (geteuid () != 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_ROOT,
+                     "Not running as root, cannot query DM maps");
+        return NULL;
+    }
+
+    lvdata = bd_lvm_lvinfo (vg_name, cached_lv, error);
+    if (!lvdata)
+        return NULL;
+
+    pool = dm_pool_create ("bd-pool", 20);
+
+    if (g_strcmp0 (lvdata->segtype, "thin-pool") == 0)
+        map_name = dm_build_dm_name (pool, vg_name, lvdata->data_lv, NULL);
+    else
+        /* translate the VG+LV name into the DM map name */
+        map_name = dm_build_dm_name (pool, vg_name, cached_lv, NULL);
+
+    bd_lvm_lvdata_free (lvdata);
+
+    task = dm_task_create (DM_DEVICE_STATUS);
+    if (!task) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to create DM task for the cache map '%s': ", map_name);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (dm_task_set_name (task, map_name) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to create DM task for the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (dm_task_run (task) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to run the DM task for the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (dm_task_get_info (task, &info) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to get task info for the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (!info.exists) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_NOCACHE,
+                     "The cache map '%s' doesn't exist: ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    dm_get_next_target (task, NULL, &start, &length, &type, &params);
+
+    if (dm_get_status_cache (pool, params, &status) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Failed to get status of the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    ret = g_new0 (BDLVMCacheStats, 1);
+    ret->block_size = status->block_size * SECTOR_SIZE;
+    ret->cache_size = status->total_blocks * ret->block_size;
+    ret->cache_used = status->used_blocks * ret->block_size;
+
+    ret->md_block_size = status->metadata_block_size * SECTOR_SIZE;
+    ret->md_size = status->metadata_total_blocks * ret->md_block_size;
+    ret->md_used = status->metadata_used_blocks * ret->md_block_size;
+
+    ret->read_hits = status->read_hits;
+    ret->read_misses = status->read_misses;
+    ret->write_hits = status->write_hits;
+    ret->write_misses = status->write_misses;
+
+    if (status->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH)
+        ret->mode = BD_LVM_CACHE_MODE_WRITETHROUGH;
+    else if (status->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
+        ret->mode = BD_LVM_CACHE_MODE_WRITEBACK;
+    else {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                      "Failed to determine status of the cache from '%"G_GUINT64_FORMAT"': ",
+                      status->feature_flags);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        bd_lvm_cache_stats_free (ret);
+        return NULL;
+    }
+
+    dm_task_destroy (task);
+    dm_pool_destroy (pool);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_thpool_convert:
+ * @vg_name: name of the VG to create the new thin pool in
+ * @data_lv: name of the LV that should become the data part of the new pool
+ * @metadata_lv: name of the LV that should become the metadata part of the new pool
+ * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Converts the @data_lv and @metadata_lv into a new thin pool in the @vg_name
+ * VG.
+ *
+ * Returns: whether the new thin pool was successfully created from @data_lv and
+ *          @metadata_lv or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thpool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    gchar *obj_id = NULL;
+    gchar *data_lv_path = NULL;
+    gchar *metadata_lv_path = NULL;
+    gboolean ret = FALSE;
+
+    obj_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
+    data_lv_path = get_object_path (obj_id, error);
+    g_free (obj_id);
+    if (!data_lv_path)
+        return FALSE;
+
+    obj_id = g_strdup_printf ("%s/%s", vg_name, metadata_lv);
+    metadata_lv_path = get_object_path (obj_id, error);
+    g_free (obj_id);
+    if (!metadata_lv_path)
+        return FALSE;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("o", metadata_lv_path));
+    g_variant_builder_add_value (&builder, g_variant_new ("o", data_lv_path));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    ret = call_lvm_obj_method_sync (vg_name, VG_INTF, "CreateThinPool", params, NULL, extra, TRUE, error);
+    if (ret && name)
+        bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
+    return ret;
+}
+
+/**
+ * bd_lvm_cache_pool_convert:
+ * @vg_name: name of the VG to create the new thin pool in
+ * @data_lv: name of the LV that should become the data part of the new pool
+ * @metadata_lv: name of the LV that should become the metadata part of the new pool
+ * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Converts the @data_lv and @metadata_lv into a new cache pool in the @vg_name
+ * VG.
+ *
+ * Returns: whether the new cache pool was successfully created from @data_lv and
+ *          @metadata_lv or not
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_cache_pool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    gchar *obj_id = NULL;
+    gchar *data_lv_path = NULL;
+    gchar *metadata_lv_path = NULL;
+    gboolean ret = FALSE;
+
+    obj_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
+    data_lv_path = get_object_path (obj_id, error);
+    g_free (obj_id);
+    if (!data_lv_path)
+        return FALSE;
+
+    obj_id = g_strdup_printf ("%s/%s", vg_name, metadata_lv);
+    metadata_lv_path = get_object_path (obj_id, error);
+    g_free (obj_id);
+    if (!metadata_lv_path)
+        return FALSE;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+    g_variant_builder_add_value (&builder, g_variant_new ("o", metadata_lv_path));
+    g_variant_builder_add_value (&builder, g_variant_new ("o", data_lv_path));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    ret = call_lvm_obj_method_sync (vg_name, VG_INTF, "CreateCachePool", params, NULL, extra, TRUE, error);
+
+    if (!ret && name)
+        bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_vdo_pool_create:
+ * @vg_name: name of the VG to create a new LV in
+ * @lv_name: name of the to-be-created VDO LV
+ * @pool_name: (nullable): name of the to-be-created VDO pool LV or %NULL for default name
+ * @data_size: requested size of the data VDO LV (physical size of the @pool_name VDO pool LV)
+ * @virtual_size: requested virtual_size of the @lv_name VDO LV
+ * @index_memory: amount of index memory (in bytes) or 0 for default
+ * @compression: whether to enable compression or not
+ * @deduplication: whether to enable deduplication or not
+ * @write_policy: write policy for the volume
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the given @vg_name/@lv_name VDO LV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_vdo_pool_create (const gchar *vg_name, const gchar *lv_name, const gchar *pool_name, guint64 data_size, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
+    GVariantBuilder builder;
+    GVariant *params = NULL;
+    GVariant *extra_params = NULL;
+    gchar *old_config = NULL;
+    const gchar *write_policy_str = NULL;
+    g_autofree gchar *name = NULL;
+    gboolean ret = FALSE;
+
+    write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
+    if (write_policy_str == NULL)
+        return FALSE;
+
+    /* build the params tuple */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+
+    if (!pool_name) {
+        name = g_strdup_printf ("%s_vpool", lv_name);
+        g_variant_builder_add_value (&builder, g_variant_new ("s", name));
+    } else
+        g_variant_builder_add_value (&builder, g_variant_new ("s", pool_name));
+
+    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
+    g_variant_builder_add_value (&builder, g_variant_new ("t", data_size));
+    g_variant_builder_add_value (&builder, g_variant_new ("t", virtual_size));
+    params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    /* and now the extra_params params */
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
+    g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--compression", g_variant_new ("s", compression ? "y" : "n")));
+    g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--deduplication", g_variant_new ("s", deduplication ? "y" : "n")));
+    extra_params = g_variant_builder_end (&builder);
+    g_variant_builder_clear (&builder);
+
+    /* index_memory and write_policy can be specified only using the config */
+    g_mutex_lock (&global_config_lock);
+    old_config = global_config_str;
+    if (index_memory != 0)
+        global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
+                                                                                                                                     index_memory / (1024 * 1024),
+                                                                                                                                     write_policy_str);
+    else
+        global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
+                                                                                        write_policy_str);
+
+    ret = call_lvm_obj_method_sync (vg_name, VG_VDO_INTF, "CreateVdoPoolandLv", params, extra_params, extra, FALSE, error);
+
+    g_free (global_config_str);
+    global_config_str = old_config;
+    g_mutex_unlock (&global_config_lock);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_vdo_enable_compression:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to enable compression on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether compression was successfully enabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_enable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return call_vdopool_method_sync (vg_name, pool_name, "EnableCompression", NULL, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vdo_disable_compression:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to disable compression on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether compression was successfully disabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_disable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return call_vdopool_method_sync (vg_name, pool_name, "DisableCompression", NULL, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vdo_enable_deduplication:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to enable deduplication on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether deduplication was successfully enabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_enable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return call_vdopool_method_sync (vg_name, pool_name, "EnableDeduplication", NULL, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vdo_enable_deduplication:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to disable deduplication on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether deduplication was successfully disabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_disable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return call_vdopool_method_sync (vg_name, pool_name, "DisableDeduplication", NULL, NULL, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vdo_info:
+ * @vg_name: name of the VG that contains the LV to get information about
+ * @pool_name: name of the VDO pool LV to get information about
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
+ * of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMVDOPooldata* bd_lvm_vdo_info (const gchar *vg_name, const gchar *pool_name, GError **error) {
+    GVariant *props = NULL;
+
+    props = get_vdo_properties (vg_name, pool_name, error);
+    if (!props)
+        /* the error is already populated */
+        return NULL;
+
+    return get_vdo_data_from_props (props, error);
+}
+
+/**
+ * bd_lvm_vdo_resize:
+ * @vg_name: name of the VG containing the to-be-resized VDO LV
+ * @lv_name: name of the to-be-resized VDO LV
+ * @size: the requested new size of the VDO LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name VDO LV was successfully resized or not
+ *
+ * Note: Reduction needs to process TRIM for reduced disk area to unmap used data blocks
+ *       from the VDO pool LV and it may take a long time.
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_resize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    return bd_lvm_lvresize (vg_name, lv_name, size, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_pool_resize:
+ * @vg_name: name of the VG containing the to-be-resized VDO pool LV
+ * @pool_name: name of the to-be-resized VDO pool LV
+ * @size: the requested new size of the VDO pool LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool LV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@pool_name VDO pool LV was successfully resized or not
+ *
+ * Note: Size of the VDO pool LV can be only extended, not reduced.
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_pool_resize (const gchar *vg_name, const gchar *pool_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    BDLVMLVdata *info = NULL;
+
+    info = bd_lvm_lvinfo (vg_name, pool_name, error);
+    if (!info)
+        return FALSE;
+
+    if (info->size >= size) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_SUPPORTED,
+                     "Reducing physical size of the VDO pool LV is not supported.");
+        bd_lvm_lvdata_free (info);
+        return FALSE;
+    }
+
+    bd_lvm_lvdata_free (info);
+
+    return bd_lvm_lvresize (vg_name, pool_name, size, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_pool_convert:
+ * @vg_name: name of the VG that contains @pool_lv
+ * @pool_lv: name of the LV that should become the new VDO pool LV
+ * @name: (nullable): name for the VDO LV or %NULL for default name
+ * @virtual_size: virtual size for the new VDO LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Converts the @pool_lv into a new VDO pool LV in the @vg_name VG and creates a new
+ * @name VDO LV with size @virtual_size.
+ *
+ * Note: All data on @pool_lv will be irreversibly destroyed.
+ *
+ * Returns: whether the new VDO pool LV was successfully created from @pool_lv and or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE&%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_pool_convert (const gchar *vg_name G_GNUC_UNUSED, const gchar *pool_lv G_GNUC_UNUSED, const gchar *name G_GNUC_UNUSED,
+                                  guint64 virtual_size G_GNUC_UNUSED, guint64 index_memory G_GNUC_UNUSED, gboolean compression G_GNUC_UNUSED,
+                                  gboolean deduplication G_GNUC_UNUSED, BDLVMVDOWritePolicy write_policy G_GNUC_UNUSED,
+                                  const BDExtraArg **extra G_GNUC_UNUSED, GError **error) {
+    return bd_lvm_is_tech_avail (BD_LVM_TECH_VDO, BD_LVM_TECH_MODE_CREATE | BD_LVM_TECH_MODE_MODIFY, error);
+}
+
+/**
+ * bd_lvm_vdolvpoolname:
+ * @vg_name: name of the VG containing the queried VDO LV
+ * @lv_name: name of the queried VDO LV
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
+ * VDO LV or %NULL if failed to determine (@error) is set in those cases
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_vdolvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    GVariant *prop = NULL;
+    const gchar *segtype = NULL;
+    gchar *pool_obj_path = NULL;
+    gchar *ret = NULL;
+
+    prop = get_lv_property (vg_name, lv_name, "SegType", error);
+    if (!prop)
+        return NULL;
+
+    g_variant_get_child (prop, 0, "&s", &segtype);
+    if (g_strcmp0 (segtype, "vdo") != 0) {
+        g_variant_unref (prop);
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
+                     "The LV '%s' is not a VDO LV and thus have no VDO pool", lv_name);
+        return NULL;
+    }
+    g_variant_unref (prop);
+
+    prop = get_lv_property (vg_name, lv_name, "PoolLv", error);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "o", &pool_obj_path);
+    g_variant_unref (prop);
+
+    prop = get_object_property (pool_obj_path, LV_CMN_INTF, "Name", error);
+    g_free (pool_obj_path);
+    if (!prop)
+        return NULL;
+    g_variant_get (prop, "s", &ret);
+    g_variant_unref (prop);
+
+    return ret;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -pruN 3.3.1-3/src/plugins/lvm/lvm-private.h 3.4.0-1/src/plugins/lvm/lvm-private.h
--- 3.3.1-3/src/plugins/lvm/lvm-private.h	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/lvm-private.h	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,17 @@
+#ifndef BD_LVM_PRIVATE
+#define BD_LVM_PRIVATE
+
+#define SECTOR_SIZE 512
+#define DEFAULT_PE_SIZE (4 MiB)
+#define USE_DEFAULT_PE_SIZE 0
+#define RESOLVE_PE_SIZE(size) ((size) == USE_DEFAULT_PE_SIZE ? DEFAULT_PE_SIZE : (size))
+
+#define LVM_MIN_VERSION "2.02.116"
+#define LVM_VERSION_FSRESIZE "2.03.19"
+
+extern GMutex global_config_lock;
+extern gchar *global_config_str;
+
+extern gchar *global_devices_str;
+
+#endif /* BD_LVM_PRIVATE */
diff -pruN 3.3.1-3/src/plugins/lvm/lvm.c 3.4.0-1/src/plugins/lvm/lvm.c
--- 3.3.1-3/src/plugins/lvm/lvm.c	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/lvm.c	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,3277 @@
+/*
+ * Copyright (C) 2014  Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vratislav Podzimek <vpodzime@redhat.com>
+ */
+
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+#include <unistd.h>
+#include <blockdev/utils.h>
+#include <libdevmapper.h>
+
+#include "lvm.h"
+#include "lvm-private.h"
+#include "check_deps.h"
+#include "dm_logging.h"
+#include "vdo_stats.h"
+
+/**
+ * SECTION: lvm
+ * @short_description: plugin for operations with LVM
+ * @title: LVM
+ * @include: lvm.h
+ *
+ * A plugin for operations with LVM. All sizes passed in/out to/from
+ * the functions are in bytes.
+ */
+
+/**
+ * bd_lvm_error_quark: (skip)
+ */
+GQuark bd_lvm_error_quark (void)
+{
+    return g_quark_from_static_string ("g-bd-lvm-error-quark");
+}
+
+BDLVMPVdata* bd_lvm_pvdata_copy (BDLVMPVdata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMPVdata *new_data = g_new0 (BDLVMPVdata, 1);
+
+    new_data->pv_name = g_strdup (data->pv_name);
+    new_data->pv_uuid = g_strdup (data->pv_uuid);
+    new_data->pv_free = data->pv_free;
+    new_data->pv_size = data->pv_size;
+    new_data->pe_start = data->pe_start;
+    new_data->vg_name = g_strdup (data->vg_name);
+    new_data->vg_uuid = g_strdup (data->vg_uuid);
+    new_data->vg_size = data->vg_size;
+    new_data->vg_free = data->vg_free;
+    new_data->vg_extent_size = data->vg_extent_size;
+    new_data->vg_extent_count = data->vg_extent_count;
+    new_data->vg_free_count = data->vg_free_count;
+    new_data->vg_pv_count = data->vg_pv_count;
+    new_data->pv_tags = g_strdupv (data->pv_tags);
+    new_data->missing = data->missing;
+
+    return new_data;
+}
+
+void bd_lvm_pvdata_free (BDLVMPVdata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data->pv_name);
+    g_free (data->pv_uuid);
+    g_free (data->vg_name);
+    g_free (data->vg_uuid);
+    g_strfreev (data->pv_tags);
+    g_free (data);
+}
+
+BDLVMVGdata* bd_lvm_vgdata_copy (BDLVMVGdata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMVGdata *new_data = g_new0 (BDLVMVGdata, 1);
+
+    new_data->name = g_strdup (data->name);
+    new_data->uuid = g_strdup (data->uuid);
+    new_data->size = data->size;
+    new_data->free = data->free;
+    new_data->extent_size = data->extent_size;
+    new_data->extent_count = data->extent_count;
+    new_data->free_count = data->free_count;
+    new_data->pv_count = data->pv_count;
+    new_data->vg_tags = g_strdupv (data->vg_tags);
+    return new_data;
+}
+
+void bd_lvm_vgdata_free (BDLVMVGdata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data->name);
+    g_free (data->uuid);
+    g_strfreev (data->vg_tags);
+    g_free (data);
+}
+
+BDLVMSEGdata* bd_lvm_segdata_copy (BDLVMSEGdata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMSEGdata *new_data = g_new0 (BDLVMSEGdata, 1);
+
+    new_data->size_pe = data->size_pe;
+    new_data->pv_start_pe = data->pv_start_pe;
+    new_data->pvdev = g_strdup (data->pvdev);
+    return new_data;
+}
+
+void bd_lvm_segdata_free (BDLVMSEGdata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data->pvdev);
+    g_free (data);
+}
+
+static BDLVMSEGdata **copy_segs (BDLVMSEGdata **segs) {
+    int len;
+    BDLVMSEGdata **new_segs;
+
+    if (segs == NULL)
+       return NULL;
+
+    for (len = 0; segs[len]; len++)
+        ;
+
+    new_segs = g_new0 (BDLVMSEGdata *, len+1);
+    for (int i = 0; i < len; i++)
+        new_segs[i] = bd_lvm_segdata_copy (segs[i]);
+
+    return new_segs;
+}
+
+static void free_segs (BDLVMSEGdata **segs) {
+    if (segs == NULL)
+       return;
+
+    for (int i = 0; segs[i]; i++)
+        bd_lvm_segdata_free (segs[i]);
+    (g_free) (segs);
+}
+
+BDLVMLVdata* bd_lvm_lvdata_copy (BDLVMLVdata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMLVdata *new_data = g_new0 (BDLVMLVdata, 1);
+
+    new_data->lv_name = g_strdup (data->lv_name);
+    new_data->vg_name = g_strdup (data->vg_name);
+    new_data->uuid = g_strdup (data->uuid);
+    new_data->size = data->size;
+    new_data->attr = g_strdup (data->attr);
+    new_data->segtype = g_strdup (data->segtype);
+    new_data->origin = g_strdup (data->origin);
+    new_data->pool_lv = g_strdup (data->pool_lv);
+    new_data->data_lv = g_strdup (data->data_lv);
+    new_data->metadata_lv = g_strdup (data->metadata_lv);
+    new_data->roles = g_strdup (data->roles);
+    new_data->move_pv = g_strdup (data->move_pv);
+    new_data->data_percent = data->data_percent;
+    new_data->metadata_percent = data->metadata_percent;
+    new_data->copy_percent = data->copy_percent;
+    new_data->lv_tags = g_strdupv (data->lv_tags);
+    new_data->data_lvs = g_strdupv (data->data_lvs);
+    new_data->metadata_lvs = g_strdupv (data->metadata_lvs);
+    new_data->segs = copy_segs (data->segs);
+    return new_data;
+}
+
+void bd_lvm_lvdata_free (BDLVMLVdata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data->lv_name);
+    g_free (data->vg_name);
+    g_free (data->uuid);
+    g_free (data->attr);
+    g_free (data->segtype);
+    g_free (data->origin);
+    g_free (data->pool_lv);
+    g_free (data->data_lv);
+    g_free (data->metadata_lv);
+    g_free (data->roles);
+    g_free (data->move_pv);
+    g_strfreev (data->lv_tags);
+    g_strfreev (data->data_lvs);
+    g_strfreev (data->metadata_lvs);
+    free_segs (data->segs);
+    g_free (data);
+}
+
+BDLVMVDOPooldata* bd_lvm_vdopooldata_copy (BDLVMVDOPooldata *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMVDOPooldata *new_data = g_new0 (BDLVMVDOPooldata, 1);
+
+    new_data->operating_mode = data->operating_mode;
+    new_data->compression_state = data->compression_state;
+    new_data->index_state = data->index_state;
+    new_data->write_policy = data->write_policy;
+    new_data->used_size = data->used_size;
+    new_data->saving_percent = data->saving_percent;
+    new_data->index_memory_size = data->index_memory_size;
+    new_data->deduplication = data->deduplication;
+    new_data->compression = data->compression;
+    return new_data;
+}
+
+void bd_lvm_vdopooldata_free (BDLVMVDOPooldata *data) {
+    if (data == NULL)
+        return;
+
+    g_free (data);
+}
+
+BDLVMCacheStats* bd_lvm_cache_stats_copy (BDLVMCacheStats *data) {
+    if (data == NULL)
+        return NULL;
+
+    BDLVMCacheStats *new = g_new0 (BDLVMCacheStats, 1);
+
+    new->block_size = data->block_size;
+    new->cache_size = data->cache_size;
+    new->cache_used = data->cache_used;
+    new->md_block_size = data->md_block_size;
+    new->md_size = data->md_size;
+    new->md_used = data->md_used;
+    new->read_hits = data->read_hits;
+    new->read_misses = data->read_misses;
+    new->write_hits = data->write_hits;
+    new->write_misses = data->write_misses;
+    new->mode = data->mode;
+
+    return new;
+}
+
+void bd_lvm_cache_stats_free (BDLVMCacheStats *data) {
+    g_free (data);
+}
+
+
+static volatile guint avail_deps = 0;
+static volatile guint avail_features = 0;
+static volatile guint avail_module_deps = 0;
+static GMutex deps_check_lock;
+
+#define DEPS_LVM 0
+#define DEPS_LVM_MASK (1 << DEPS_LVM)
+#define DEPS_LVMDEVICES 1
+#define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES)
+#define DEPS_LVMCONFIG 2
+#define DEPS_LVMCONFIG_MASK (1 << DEPS_LVMCONFIG)
+#define DEPS_LAST 3
+
+static const UtilDep deps[DEPS_LAST] = {
+    {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"},
+    {"lvmdevices", NULL, NULL, NULL},
+    {"lvmconfig", "2.03.17", "--version", "LVM version:\\s+([\\d\\.]+)"},
+};
+
+#define FEATURES_VDO 0
+#define FEATURES_VDO_MASK (1 << FEATURES_VDO)
+#define FEATURES_WRITECACHE 0
+#define FEATURES_WRITECACHE_MASK (1 << FEATURES_WRITECACHE)
+#define FEATURES_LAST 2
+
+static const UtilFeatureDep features[FEATURES_LAST] = {
+    {"lvm", "vdo", "segtypes", NULL},
+    {"lvm", "writecache", "segtypes", NULL},
+};
+
+#define MODULE_DEPS_VDO 0
+#define MODULE_DEPS_VDO_MASK (1 << MODULE_DEPS_VDO)
+#define MODULE_DEPS_LAST 1
+
+static const gchar*const module_deps[MODULE_DEPS_LAST] = { "dm-vdo" };
+
+
+/**
+ * bd_lvm_init:
+ *
+ * Initializes the plugin. **This function is called automatically by the
+ * library's initialization functions.**
+ *
+ */
+gboolean bd_lvm_init (void) {
+    dm_log_with_errno_init ((dm_log_with_errno_fn) redirect_dm_log);
+#ifdef DEBUG
+    dm_log_init_verbose (LOG_DEBUG);
+#else
+    dm_log_init_verbose (LOG_INFO);
+#endif
+
+    return TRUE;
+};
+
+/**
+ * bd_lvm_close:
+ *
+ * Cleans up after the plugin. **This function is called automatically by the
+ * library's functions that unload it.**
+ *
+ */
+void bd_lvm_close (void) {
+    dm_log_with_errno_init (NULL);
+    dm_log_init_verbose (0);
+}
+
+/**
+ * bd_lvm_is_tech_avail:
+ * @tech: the queried tech
+ * @mode: a bit mask of queried modes of operation (#BDLVMTechMode) for @tech
+ * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
+ *
+ * Returns: whether the @tech-@mode combination is available -- supported by the
+ *          plugin implementation and having all the runtime dependencies available
+ */
+gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) {
+    switch (tech) {
+    case BD_LVM_TECH_THIN_CALCS:
+        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
+            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
+                         "Only 'query' supported for thin calculations");
+            return FALSE;
+        } else
+            return TRUE;
+    case BD_LVM_TECH_CALCS:
+        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
+            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
+                         "Only 'query' supported for calculations");
+            return FALSE;
+        } else
+            return TRUE;
+    case BD_LVM_TECH_VDO:
+            return check_features (&avail_features, FEATURES_VDO_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
+                   check_module_deps (&avail_module_deps, MODULE_DEPS_VDO_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error) &&
+                   check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    case BD_LVM_TECH_WRITECACHE:
+            return check_features (&avail_features, FEATURES_WRITECACHE_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
+                   check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    case BD_LVM_TECH_DEVICES:
+            return check_deps (&avail_deps, DEPS_LVMDEVICES_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    case BD_LVM_TECH_CONFIG:
+        return check_deps (&avail_deps, DEPS_LVMCONFIG_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    default:
+        /* everything is supported by this implementation of the plugin */
+        return check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
+    }
+}
+
+static gboolean call_lvm_and_report_error (const gchar **args, const BDExtraArg **extra, gboolean lock_config, GError **error) {
+    gboolean success = FALSE;
+    guint i = 0;
+    guint args_length = g_strv_length ((gchar **) args);
+    g_autofree gchar *config_arg = NULL;
+    g_autofree gchar *devices_arg = NULL;
+
+    if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
+        return FALSE;
+
+    /* don't allow global config string changes during the run */
+    if (lock_config)
+        g_mutex_lock (&global_config_lock);
+
+    /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
+    const gchar **argv = g_new0 (const gchar*, args_length + 4);
+
+    /* construct argv from args with "lvm" prepended */
+    argv[0] = "lvm";
+    for (i=0; i < args_length; i++)
+        argv[i+1] = args[i];
+    if (global_config_str) {
+        config_arg = g_strdup_printf ("--config=%s", global_config_str);
+        argv[++args_length] = config_arg;
+    }
+    if (global_devices_str) {
+        devices_arg = g_strdup_printf ("--devices=%s", global_devices_str);
+        argv[++args_length] = devices_arg;
+    }
+    argv[++args_length] = NULL;
+
+    success = bd_utils_exec_and_report_error (argv, extra, error);
+    if (lock_config)
+        g_mutex_unlock (&global_config_lock);
+    g_free (argv);
+
+    return success;
+}
+
+static gboolean call_lvm_and_capture_output (const gchar **args, const BDExtraArg **extra, gchar **output, GError **error) {
+    gboolean success = FALSE;
+    guint i = 0;
+    guint args_length = g_strv_length ((gchar **) args);
+    g_autofree gchar *config_arg = NULL;
+    g_autofree gchar *devices_arg = NULL;
+
+    if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
+        return FALSE;
+
+    /* don't allow global config string changes during the run */
+    g_mutex_lock (&global_config_lock);
+
+    /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
+    const gchar **argv = g_new0 (const gchar*, args_length + 4);
+
+    /* construct argv from args with "lvm" prepended */
+    argv[0] = "lvm";
+    for (i=0; i < args_length; i++)
+        argv[i+1] = args[i];
+    if (global_config_str) {
+        config_arg = g_strdup_printf ("--config=%s", global_config_str);
+        argv[++args_length] = config_arg;
+    }
+    if (global_devices_str) {
+        devices_arg = g_strdup_printf ("--devices=%s", global_devices_str);
+        argv[++args_length] = devices_arg;
+    }
+    argv[++args_length] = NULL;
+
+    success = bd_utils_exec_and_capture_output (argv, extra, output, error);
+    g_mutex_unlock (&global_config_lock);
+    g_free (argv);
+
+    return success;
+}
+
+/**
+ * parse_lvm_vars:
+ * @str: string to parse
+ * @num_items: (out): number of parsed items
+ *
+ * Returns: (transfer full): a GHashTable containing key-value items parsed from the @string
+ */
+static GHashTable* parse_lvm_vars (const gchar *str, guint *num_items) {
+    GHashTable *table = NULL;
+    gchar **items = NULL;
+    gchar **item_p = NULL;
+    gchar **key_val = NULL;
+
+    table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    *num_items = 0;
+
+    items = g_strsplit_set (str, " \t\n", 0);
+    for (item_p=items; *item_p; item_p++) {
+        key_val = g_strsplit (*item_p, "=", 2);
+        if (g_strv_length (key_val) == 2) {
+            /* we only want to process valid lines (with the '=' character) */
+            g_hash_table_insert (table, key_val[0], key_val[1]);
+            g_free (key_val);
+            (*num_items)++;
+        } else
+            /* invalid line, just free key_val */
+            g_strfreev (key_val);
+    }
+
+    g_strfreev (items);
+    return table;
+}
+
+static BDLVMPVdata* get_pv_data_from_table (GHashTable *table, gboolean free_table) {
+    BDLVMPVdata *data = g_new0 (BDLVMPVdata, 1);
+    gchar *value = NULL;
+
+    data->pv_name = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_PV_NAME"));
+    data->pv_uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_PV_UUID"));
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_FREE");
+    if (value)
+        data->pv_free = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->pv_free = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_SIZE");
+    if (value)
+        data->pv_size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->pv_size = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_PE_START");
+    if (value)
+        data->pe_start = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->pe_start = 0;
+
+    data->vg_name = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_VG_NAME"));
+    data->vg_uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_VG_UUID"));
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_SIZE");
+    if (value)
+        data->vg_size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->vg_size = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE");
+    if (value)
+        data->vg_free = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->vg_free = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_SIZE");
+    if (value)
+        data->vg_extent_size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->vg_extent_size = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_COUNT");
+    if (value)
+        data->vg_extent_count = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->vg_extent_count = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE_COUNT");
+    if (value)
+        data->vg_free_count = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->vg_free_count = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_COUNT");
+    if (value)
+        data->vg_pv_count = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->vg_pv_count = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_TAGS");
+    if (value)
+        data->pv_tags = g_strsplit (value, ",", -1);
+    else
+        data->pv_tags = NULL;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_MISSING");
+    data->missing = (g_strcmp0 (value, "missing") == 0);
+
+    if (free_table)
+        g_hash_table_destroy (table);
+
+    return data;
+}
+
+static BDLVMVGdata* get_vg_data_from_table (GHashTable *table, gboolean free_table) {
+    BDLVMVGdata *data = g_new0 (BDLVMVGdata, 1);
+    gchar *value = NULL;
+
+    data->name = g_strdup (g_hash_table_lookup (table, "LVM2_VG_NAME"));
+    data->uuid = g_strdup (g_hash_table_lookup (table, "LVM2_VG_UUID"));
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_SIZE");
+    if (value)
+        data->size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->size = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE");
+    if (value)
+        data->free = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->free= 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_SIZE");
+    if (value)
+        data->extent_size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->extent_size = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_COUNT");
+    if (value)
+        data->extent_count = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->extent_count = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE_COUNT");
+    if (value)
+        data->free_count = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->free_count = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_COUNT");
+    if (value)
+        data->pv_count = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->pv_count = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXPORTED");
+    if (value && g_strcmp0 (value, "exported") == 0)
+        data->exported = TRUE;
+    else
+        data->exported = FALSE;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_TAGS");
+    if (value)
+        data->vg_tags = g_strsplit (value, ",", -1);
+    else
+        data->vg_tags = NULL;
+
+    if (free_table)
+        g_hash_table_destroy (table);
+
+    return data;
+}
+
+static gchar **prepare_sublvs (gchar **values, gchar *extra_value) {
+  /* LVM2 guarantees: No "/dev/" prefixes or "[unknown]" in a list of sub-lvs. */
+  gboolean found_extra = FALSE;
+  for (int i = 0; values[i]; i++) {
+    gchar *paren = strrchr (values[i], '(');
+    if (paren) {
+      /* LVM2 guarantees: start offsets of sub-lvs are always zero. */
+      *paren = '\0';
+    }
+    if (g_strcmp0 (extra_value, values[i]) == 0)
+      found_extra = TRUE;
+  }
+  if (extra_value && *extra_value && !found_extra) {
+    int len = g_strv_length (values);
+    gchar **new_values = g_new0 (gchar *, len+2);
+    for (int j = 0; j < len; j++)
+      new_values[j] = values[j];
+    new_values[len] = g_strdup (extra_value);
+    g_free (values);
+    values = new_values;
+  }
+  return values;
+}
+
+static BDLVMLVdata* get_lv_data_from_table (GHashTable *table, gboolean free_table) {
+    BDLVMLVdata *data = g_new0 (BDLVMLVdata, 1);
+    gchar *value = NULL;
+
+    data->lv_name = g_strdup (g_hash_table_lookup (table, "LVM2_LV_NAME"));
+    data->vg_name = g_strdup (g_hash_table_lookup (table, "LVM2_VG_NAME"));
+    data->uuid = g_strdup (g_hash_table_lookup (table, "LVM2_LV_UUID"));
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_LV_SIZE");
+    if (value)
+        data->size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->size = 0;
+
+    data->attr = g_strdup (g_hash_table_lookup (table, "LVM2_LV_ATTR"));
+
+    value = g_hash_table_lookup (table, "LVM2_SEGTYPE");
+    if (g_strcmp0 (value, "error") == 0) {
+      /* A segment type "error" appears when "vgreduce
+       * --removemissing" replaces a missing PV with a device mapper
+       * "error" target.  It very likely was a "linear" segment before that
+       * and will again be "linear" after repair.  Let's not expose
+       * this implementation detail.
+       */
+      value = "linear";
+    }
+    data->segtype = g_strdup (value);
+
+    data->origin = g_strdup (g_hash_table_lookup (table, "LVM2_ORIGIN"));
+    data->pool_lv = g_strdup (g_hash_table_lookup (table, "LVM2_POOL_LV"));
+    data->data_lv = g_strdup (g_hash_table_lookup (table, "LVM2_DATA_LV"));
+    data->metadata_lv = g_strdup (g_hash_table_lookup (table, "LVM2_METADATA_LV"));
+    data->roles = g_strdup (g_hash_table_lookup (table, "LVM2_LV_ROLE"));
+
+    data->move_pv = g_strdup (g_hash_table_lookup (table, "LVM2_MOVE_PV"));
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_DATA_PERCENT");
+    if (value)
+        data->data_percent = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->data_percent = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_METADATA_PERCENT");
+    if (value)
+        data->metadata_percent = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->metadata_percent = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_COPY_PERCENT");
+    if (value)
+        data->copy_percent = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->copy_percent = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_LV_TAGS");
+    if (value)
+        data->lv_tags = g_strsplit (value, ",", -1);
+    else
+        data->lv_tags = NULL;
+
+    /* replace '[' and ']' (marking LVs as internal) with spaces and then
+       remove all the leading and trailing whitespace */
+    g_strstrip (g_strdelimit (data->pool_lv, "[]", ' '));
+    g_strstrip (g_strdelimit (data->data_lv, "[]", ' '));
+    g_strstrip (g_strdelimit (data->metadata_lv, "[]", ' '));
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_DEVICES");
+    if (value) {
+      gchar **values = g_strsplit (value, ",", -1);
+
+      /* If values starts with "/dev/", we have a single PV.
+
+         If the list is empty, this is probably an "error" segment
+         resulting from a "vgreduce --removemissing" operation.
+
+         If the value starts with "[unknown]", it is a segment with a
+         missing PV that hasn't been converted to an "error" segment
+         yet.
+
+         Otherwise it is a list of sub-lvs.
+
+         LVM2 guarantees: only one entry if the first is a PV
+         Additional segments are added in merge_lv_data below.
+      */
+      if (!values[0] || g_str_has_prefix (values[0], "[unknown]")) {
+        data->segs = g_new0 (BDLVMSEGdata *, 1);
+        data->segs[0] = NULL;
+        g_strfreev (values);
+      } else if (g_str_has_prefix (values[0], "/dev/")) {
+        data->segs = g_new0 (BDLVMSEGdata *, 2);
+        data->segs[0] = g_new0 (BDLVMSEGdata, 1);
+        data->segs[1] = NULL;
+
+        gchar *paren = strrchr (values[0], '(');
+        if (paren) {
+          data->segs[0]->pv_start_pe = atoi (paren+1);
+          *paren = '\0';
+        }
+        data->segs[0]->pvdev = g_strdup (values[0]);
+        value = (gchar*) g_hash_table_lookup (table, "LVM2_SEG_SIZE_PE");
+        if (value)
+          data->segs[0]->size_pe = g_ascii_strtoull (value, NULL, 0);
+        g_strfreev (values);
+      } else {
+        data->data_lvs = prepare_sublvs (values, data->data_lv);
+        value = (gchar*) g_hash_table_lookup (table, "LVM2_METADATA_DEVICES");
+        data->metadata_lvs = prepare_sublvs (g_strsplit (value ?: "", ",", -1), data->metadata_lv);
+      }
+    }
+
+    if (free_table)
+        g_hash_table_destroy (table);
+
+    return data;
+}
+
+static void merge_lv_data (BDLVMLVdata *data, BDLVMLVdata *more_data) {
+  /* LVM2 guarantees:
+     - more_data->data_lvs is NULL
+     - more_data->metadata_lvs is NULL
+     - more_data->segs has zero or one entry
+     - more_data->seg_type is the same as data->seg_type (after mapping "error" to "linear")
+  */
+
+  if (more_data->segs && more_data->segs[0]) {
+    int i;
+    for (i = 0; data->segs && data->segs[i]; i++)
+      ;
+
+    BDLVMSEGdata **new_segs = g_new0 (BDLVMSEGdata *, i+2);
+    for (i = 0; data->segs && data->segs[i]; i++)
+      new_segs[i] = data->segs[i];
+    new_segs[i] = more_data->segs[0];
+    g_free (data->segs);
+    data->segs = new_segs;
+    more_data->segs[0] = NULL;
+  }
+}
+
+static BDLVMVDOPooldata* get_vdo_data_from_table (GHashTable *table, gboolean free_table) {
+    BDLVMVDOPooldata *data = g_new0 (BDLVMVDOPooldata, 1);
+    gchar *value = NULL;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_OPERATING_MODE");
+    if (g_strcmp0 (value, "recovering") == 0)
+        data->operating_mode = BD_LVM_VDO_MODE_RECOVERING;
+    else if (g_strcmp0 (value, "read-only") == 0)
+        data->operating_mode = BD_LVM_VDO_MODE_READ_ONLY;
+    else if (g_strcmp0 (value, "normal") == 0)
+        data->operating_mode = BD_LVM_VDO_MODE_NORMAL;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO operating mode: %s", value);
+        data->operating_mode = BD_LVM_VDO_MODE_UNKNOWN;
+    }
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_COMPRESSION_STATE");
+    if (g_strcmp0 (value, "online") == 0)
+        data->compression_state = BD_LVM_VDO_COMPRESSION_ONLINE;
+    else if (g_strcmp0 (value, "offline") == 0)
+        data->compression_state = BD_LVM_VDO_COMPRESSION_OFFLINE;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO compression state: %s", value);
+        data->compression_state = BD_LVM_VDO_COMPRESSION_UNKNOWN;
+    }
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_INDEX_STATE");
+    if (g_strcmp0 (value, "error") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_ERROR;
+    else if (g_strcmp0 (value, "closed") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_CLOSED;
+    else if (g_strcmp0 (value, "opening") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_OPENING;
+    else if (g_strcmp0 (value, "closing") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_CLOSING;
+    else if (g_strcmp0 (value, "offline") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_OFFLINE;
+    else if (g_strcmp0 (value, "online") == 0)
+        data->index_state = BD_LVM_VDO_INDEX_ONLINE;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO index state: %s", value);
+        data->index_state = BD_LVM_VDO_INDEX_UNKNOWN;
+    }
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_WRITE_POLICY");
+    if (g_strcmp0 (value, "auto") == 0)
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_AUTO;
+    else if (g_strcmp0 (value, "sync") == 0)
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_SYNC;
+    else if (g_strcmp0 (value, "async") == 0)
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_ASYNC;
+    else {
+        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO write policy: %s", value);
+        data->write_policy = BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
+    }
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_INDEX_MEMORY_SIZE");
+    if (value)
+        data->index_memory_size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->index_memory_size = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_USED_SIZE");
+    if (value)
+        data->used_size = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->used_size = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_SAVING_PERCENT");
+    if (value)
+        data->saving_percent = g_ascii_strtoull (value, NULL, 0);
+    else
+        data->saving_percent = 0;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_COMPRESSION");
+    if (value && g_strcmp0 (value, "enabled") == 0)
+        data->compression = TRUE;
+    else
+        data->compression = FALSE;
+
+    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_DEDUPLICATION");
+    if (value && g_strcmp0 (value, "enabled") == 0)
+        data->deduplication = TRUE;
+    else
+        data->deduplication = FALSE;
+
+    if (free_table)
+        g_hash_table_destroy (table);
+
+    return data;
+}
+
+/**
+ * bd_lvm_pvcreate:
+ * @device: the device to make PV from
+ * @data_alignment: data (first PE) alignment or 0 to use the default
+ * @metadata_size: size of the area reserved for metadata or 0 to use the default
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the PV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_pvcreate (const gchar *device, guint64 data_alignment, guint64 metadata_size, const BDExtraArg **extra, GError **error) {
+    const gchar *args[5] = {"pvcreate", device, NULL, NULL, NULL};
+    guint next_arg = 2;
+    gchar *dataalign_str = NULL;
+    gchar *metadata_str = NULL;
+    gboolean ret = FALSE;
+
+    if (data_alignment != 0) {
+        dataalign_str = g_strdup_printf ("--dataalignment=%"G_GUINT64_FORMAT"K", data_alignment / 1024);
+        args[next_arg++] = dataalign_str;
+    }
+
+    if (metadata_size != 0) {
+        metadata_str = g_strdup_printf ("--metadatasize=%"G_GUINT64_FORMAT"K", metadata_size / 1024);
+        args[next_arg++] = metadata_str;
+    }
+
+    ret = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free (dataalign_str);
+    g_free (metadata_str);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_pvresize:
+ * @device: the device to resize
+ * @size: the new requested size of the PV or 0 if it should be adjusted to device's size
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the PV's size was successfully changed or not
+ *
+ * If given @size different from 0, sets the PV's size to the given value (see
+ * pvresize(8)). If given @size 0, adjusts the PV's size to the underlying
+ * block device's size.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg **extra, GError **error) {
+    gchar *size_str = NULL;
+    const gchar *args[6] = {"pvresize", "-y", NULL, NULL, NULL, NULL};
+    guint8 next_pos = 2;
+    guint8 to_free_pos = 0;
+    gboolean ret = FALSE;
+
+    if (size != 0) {
+        size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
+        args[next_pos] = "--setphysicalvolumesize";
+        next_pos++;
+        args[next_pos] = size_str;
+        to_free_pos = next_pos;
+        next_pos++;
+    }
+
+    args[next_pos] = device;
+
+    ret = call_lvm_and_report_error (args, extra, TRUE, error);
+    if (to_free_pos > 0)
+        g_free ((gchar *) args[to_free_pos]);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_pvremove:
+ * @device: the PV device to be removed/destroyed
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV removal
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the PV was successfully removed/destroyed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
+ */
+gboolean bd_lvm_pvremove (const gchar *device, const BDExtraArg **extra, GError **error) {
+    /* one has to be really persuasive to remove a PV (the double --force is not
+       bug, at least not in this code) */
+    const gchar *args[6] = {"pvremove", "--force", "--force", "--yes", device, NULL};
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+static gboolean extract_pvmove_progress (const gchar *line, guint8 *completion) {
+    gchar *num_start = NULL;
+    gint n_scanned = 0;
+    guint8 try_completion = 0;
+
+    num_start = strrchr (line, ' ');
+    if (!num_start)
+        return FALSE;
+    num_start++;
+    n_scanned = sscanf (num_start, "%hhu", &try_completion);
+    if (n_scanned == 1) {
+        *completion = try_completion;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+/**
+ * bd_lvm_pvmove:
+ * @src: the PV device to move extents off of
+ * @dest: (nullable): the PV device to move extents onto or %NULL
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV move
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the extents from the @src PV where successfully moved or not
+ *
+ * If @dest is %NULL, VG allocation rules are used for the extents from the @src
+ * PV (see pvmove(8)).
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_pvmove (const gchar *src, const gchar *dest, const BDExtraArg **extra, GError **error) {
+    const gchar *args[6] = {"pvmove", "-i", "1", src, NULL, NULL};
+    gint status = 0;
+    if (dest)
+        args[4] = dest;
+
+    return bd_utils_exec_and_report_progress (args, extra, extract_pvmove_progress, &status, error);
+}
+
+/**
+ * bd_lvm_pvscan:
+ * @device: (nullable): the device to scan for PVs or %NULL
+ * @update_cache: whether to update the lvmetad cache or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the PV scan
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the system or @device was successfully scanned for PVs or not
+ *
+ * The @device argument is used only if @update_cache is %TRUE. Otherwise the
+ * whole system is scanned for PVs.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_pvscan (const gchar *device, gboolean update_cache, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"pvscan", NULL, NULL, NULL};
+    if (update_cache) {
+        args[1] = "--cache";
+        args[2] = device;
+    }
+    else
+        if (device)
+            bd_utils_log_format (BD_UTILS_LOG_WARNING, "Ignoring the device argument in pvscan (cache update not requested)");
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+static gboolean _manage_lvm_tags (const gchar *devspec, const gchar **tags, const gchar *action, const gchar *cmd, GError **error) {
+    guint tags_len = g_strv_length ((gchar **) tags);
+    const gchar **argv = g_new0 (const gchar*, 2 * tags_len + 3);
+    guint next_arg = 0;
+    gboolean success = FALSE;
+
+    argv[next_arg++] = cmd;
+    for (guint i = 0; i < tags_len; i++) {
+        argv[next_arg++] = action;
+        argv[next_arg++] = tags[i];
+    }
+    argv[next_arg++] = devspec;
+    argv[next_arg] = NULL;
+
+    success = call_lvm_and_report_error (argv, NULL, TRUE, error);
+    g_free (argv);
+    return success;
+}
+
+/**
+ * bd_lvm_add_pv_tags:
+ * @device: the device to set PV tags for
+ * @tags: (array zero-terminated=1): list of tags to add
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully added to @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_add_pv_tags (const gchar *device, const gchar **tags, GError **error) {
+    return _manage_lvm_tags (device, tags, "--addtag", "pvchange", error);
+}
+
+/**
+ * bd_lvm_delete_pv_tags:
+ * @device: the device to set PV tags for
+ * @tags: (array zero-terminated=1): list of tags to remove
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully removed from @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_delete_pv_tags (const gchar *device, const gchar **tags, GError **error) {
+    return _manage_lvm_tags (device, tags, "--deltag", "pvchange", error);
+}
+
+/**
+ * bd_lvm_pvinfo:
+ * @device: a PV to get information about or %NULL
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the PV on the given @device or
+ * %NULL in case of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMPVdata* bd_lvm_pvinfo (const gchar *device, GError **error) {
+    const gchar *args[10] = {"pvs", "--unit=b", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--noheadings",
+                       "-o", "pv_name,pv_uuid,pv_free,pv_size,pe_start,vg_name,vg_uuid,vg_size," \
+                       "vg_free,vg_extent_size,vg_extent_count,vg_free_count,pv_count,pv_tags,pv_missing",
+                       device, NULL};
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 15)) {
+            g_clear_error (error);
+            g_strfreev (lines);
+            return get_pv_data_from_table (table, TRUE);
+        } else
+            if (table)
+                g_hash_table_destroy (table);
+    }
+    g_strfreev (lines);
+
+    /* getting here means no usable info was found */
+    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                 "Failed to parse information about the PV");
+    return NULL;
+}
+
+/**
+ * bd_lvm_pvs:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about PVs found in the system
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMPVdata** bd_lvm_pvs (GError **error) {
+    const gchar *args[9] = {"pvs", "--unit=b", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--noheadings",
+                       "-o", "pv_name,pv_uuid,pv_free,pv_size,pe_start,vg_name,vg_uuid,vg_size," \
+                       "vg_free,vg_extent_size,vg_extent_count,vg_free_count,pv_count,pv_tags,pv_missing",
+                       NULL};
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+    GPtrArray *pvs;
+    BDLVMPVdata *pvdata = NULL;
+
+    pvs = g_ptr_array_new ();
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    if (!success) {
+        if (g_error_matches (*error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
+            /* no output => no VGs, not an error */
+            g_clear_error (error);
+            /* return an empty list */
+            g_ptr_array_add (pvs, NULL);
+            return (BDLVMPVdata **) g_ptr_array_free (pvs, FALSE);
+        }
+        else {
+            /* the error is already populated from the call */
+            g_ptr_array_free (pvs, TRUE);
+            return NULL;
+        }
+    }
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 15)) {
+            /* valid line, try to parse and record it */
+            pvdata = get_pv_data_from_table (table, TRUE);
+            if (pvdata)
+                g_ptr_array_add (pvs, pvdata);
+        } else
+            if (table)
+                g_hash_table_destroy (table);
+    }
+
+    g_strfreev (lines);
+
+    if (pvs->len == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                     "Failed to parse information about PVs");
+        g_ptr_array_free (pvs, TRUE);
+        return NULL;
+    }
+
+    /* returning NULL-terminated array of BDLVMPVdata */
+    g_ptr_array_add (pvs, NULL);
+    return (BDLVMPVdata **) g_ptr_array_free (pvs, FALSE);
+}
+
+/**
+ * bd_lvm_vgcreate:
+ * @name: name of the newly created VG
+ * @pv_list: (array zero-terminated=1): list of PVs the newly created VG should use
+ * @pe_size: PE size or 0 if the default value should be used
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG @name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_vgcreate (const gchar *name, const gchar **pv_list, guint64 pe_size, const BDExtraArg **extra, GError **error) {
+    guint i = 0;
+    guint pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
+    const gchar **argv = g_new0 (const gchar*, pv_list_len + 5);
+    pe_size = RESOLVE_PE_SIZE (pe_size);
+    gboolean success = FALSE;
+
+    argv[0] = "vgcreate";
+    argv[1] = "-s";
+    argv[2] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", pe_size / 1024);
+    argv[3] = name;
+    for (i=4; i < (pv_list_len + 4); i++) {
+        argv[i] = pv_list[i-4];
+    }
+    argv[i] = NULL;
+
+    success = call_lvm_and_report_error (argv, extra, TRUE, error);
+    g_free ((gchar *) argv[2]);
+    g_free (argv);
+
+    return success;
+}
+
+/**
+ * bd_lvm_vgremove:
+ * @vg_name: name of the to be removed VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG removal
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully removed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
+ */
+gboolean bd_lvm_vgremove (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"vgremove", "--force", vg_name, NULL};
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgrename:
+ * @old_vg_name: old name of the VG to rename
+ * @new_vg_name: new name for the @old_vg_name VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG rename
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully renamed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgrename (const gchar *old_vg_name, const gchar *new_vg_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"vgrename", old_vg_name, new_vg_name, NULL};
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgactivate:
+ * @vg_name: name of the to be activated VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG activation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully activated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"vgchange", "-ay", vg_name, NULL};
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgdeactivate:
+ * @vg_name: name of the to be deactivated VG
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG deactivation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG was successfully deactivated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"vgchange", "-an", vg_name, NULL};
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgextend:
+ * @vg_name: name of the to be extended VG
+ * @device: PV device to extend the @vg_name VG with
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG extension
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG @vg_name was successfully extended with the given @device or not.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"vgextend", vg_name, device, NULL};
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vgreduce:
+ * @vg_name: name of the to be reduced VG
+ * @device: (nullable): PV device the @vg_name VG should be reduced of or %NULL
+ *                        if the VG should be reduced of the missing PVs
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VG reduction
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the VG @vg_name was successfully reduced of the given @device or not
+ *
+ * Note: This function does not move extents off of the PV before removing
+ *       it from the VG. You must do that first by calling #bd_lvm_pvmove.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
+    const gchar *args[5] = {"vgreduce", NULL, NULL, NULL, NULL};
+
+    if (!device) {
+        args[1] = "--removemissing";
+        args[2] = "--force";
+        args[3] = vg_name;
+    } else {
+        args[1] = vg_name;
+        args[2] = device;
+    }
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_add_vg_tags:
+ * @vg_name: the VG to set tags on
+ * @tags: (array zero-terminated=1): list of tags to add
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully added to @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
+    return _manage_lvm_tags (vg_name, tags, "--addtag", "vgchange", error);
+}
+
+/**
+ * bd_lvm_delete_vg_tags:
+ * @vg_name: the VG to set tags on
+ * @tags: (array zero-terminated=1): list of tags to remove
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully removed from @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
+    return _manage_lvm_tags (vg_name, tags, "--deltag", "vgchange", error);
+}
+
+static gboolean _vglock_start_stop (const gchar *vg_name, gboolean start, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"vgchange", NULL, vg_name, NULL};
+
+    if (start)
+        args[1] = "--lockstart";
+    else
+        args[1] = "--lockstop";
+
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+/**
+ * bd_lvm_vglock_start:
+ * @vg_name: a shared VG to start the lockspace in lvmlockd
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the lock was successfully started for @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    return _vglock_start_stop (vg_name, TRUE, extra, error);
+}
+
+/**
+ * bd_lvm_vglock_stop:
+ * @vg_name: a shared VG to stop the lockspace in lvmlockd
+ * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
+ *                                               (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the lock was successfully stopped for @vg_name or not
+ *
+ * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
+    return _vglock_start_stop (vg_name, FALSE, extra, error);
+}
+
+/**
+ * bd_lvm_vginfo:
+ * @vg_name: a VG to get information about
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the @vg_name VG or %NULL in case
+ * of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error) {
+    const gchar *args[10] = {"vgs", "--noheadings", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--units=b",
+                       "-o", "name,uuid,size,free,extent_size,extent_count,free_count,pv_count,vg_exported,vg_tags",
+                       vg_name, NULL};
+
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 10)) {
+            g_strfreev (lines);
+            return get_vg_data_from_table (table, TRUE);
+        } else
+            if (table)
+                g_hash_table_destroy (table);
+    }
+    g_strfreev (lines);
+
+    /* getting here means no usable info was found */
+    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                 "Failed to parse information about the VG");
+    return NULL;
+}
+
+/**
+ * bd_lvm_vgs:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about VGs found in the system
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMVGdata** bd_lvm_vgs (GError **error) {
+    const gchar *args[9] = {"vgs", "--noheadings", "--nosuffix", "--nameprefixes",
+                      "--unquoted", "--units=b",
+                      "-o", "name,uuid,size,free,extent_size,extent_count,free_count,pv_count,vg_tags",
+                      NULL};
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+    GPtrArray *vgs;
+    BDLVMVGdata *vgdata = NULL;
+    GError *l_error = NULL;
+
+    vgs = g_ptr_array_new ();
+
+    success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
+    if (!success) {
+        if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
+            /* no output => no VGs, not an error */
+            g_clear_error (&l_error);
+            /* return an empty list */
+            g_ptr_array_add (vgs, NULL);
+            return (BDLVMVGdata **) g_ptr_array_free (vgs, FALSE);
+        } else {
+            /* the error is already populated from the call */
+            g_ptr_array_free (vgs, TRUE);
+            g_propagate_error (error, l_error);
+            return NULL;
+       }
+    }
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 9)) {
+            /* valid line, try to parse and record it */
+            vgdata = get_vg_data_from_table (table, TRUE);
+            if (vgdata)
+                g_ptr_array_add (vgs, vgdata);
+        } else
+            if (table)
+                g_hash_table_destroy (table);
+    }
+
+    g_strfreev (lines);
+
+    if (vgs->len == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                     "Failed to parse information about VGs");
+        g_ptr_array_free (vgs, TRUE);
+        return NULL;
+    }
+
+    /* returning NULL-terminated array of BDLVMVGdata */
+    g_ptr_array_add (vgs, NULL);
+    return (BDLVMVGdata **) g_ptr_array_free (vgs, FALSE);
+}
+
+/**
+ * bd_lvm_lvorigin:
+ * @vg_name: name of the VG containing the queried LV
+ * @lv_name: name of the queried LV
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): the origin volume for the @vg_name/@lv_name LV or
+ * %NULL if failed to determine (@error) is set in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_lvorigin (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    const gchar *args[6] = {"lvs", "--noheadings", "-o", "origin", NULL, NULL};
+    args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    g_free ((gchar *) args[4]);
+
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    return g_strstrip (output);
+}
+
+/**
+ * bd_lvm_lvcreate:
+ * @vg_name: name of the VG to create a new LV in
+ * @lv_name: name of the to-be-created LV
+ * @size: requested size of the new LV
+ * @type: (nullable): type of the new LV ("striped", "raid1",..., see lvcreate (8))
+ * @pv_list: (nullable) (array zero-terminated=1): list of PVs the newly created LV should use or %NULL
+ * if not specified
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the given @vg_name/@lv_name LV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_lvcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, const gchar *type, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
+    guint8 pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
+    const gchar **args = g_new0 (const gchar*, pv_list_len + 10);
+    gboolean success = FALSE;
+    guint64 i = 0;
+    guint64 j = 0;
+    gchar *size_str = NULL;
+    gchar *type_str = NULL;
+
+    args[i++] = "lvcreate";
+    args[i++] = "-n";
+    args[i++] = lv_name;
+    args[i++] = "-L";
+    size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
+    args[i++] = size_str;
+    args[i++] = "-y";
+    if (type) {
+        if (g_strcmp0 (type, "striped") == 0) {
+            args[i++] = "--stripes";
+            type_str = g_strdup_printf ("%d", pv_list_len);
+            args[i++] = type_str;
+        } else {
+            args[i++] = "--type";
+            args[i++] = type;
+        }
+    }
+    args[i++] = vg_name;
+
+    for (j=0; j < pv_list_len; j++)
+        args[i++] = pv_list[j];
+
+    args[i] = NULL;
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free (size_str);
+    g_free (type_str);
+    g_free (args);
+
+    return success;
+}
+
+/**
+ * bd_lvm_lvremove:
+ * @vg_name: name of the VG containing the to-be-removed LV
+ * @lv_name: name of the to-be-removed LV
+ * @force: whether to force removal or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV removal
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully removed or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
+ */
+gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean force, const BDExtraArg **extra, GError **error) {
+    /* '--yes' is needed if DISCARD is enabled */
+    const gchar *args[5] = {"lvremove", "--yes", NULL, NULL, NULL};
+    guint8 next_arg = 2;
+    gboolean success = FALSE;
+
+    if (force) {
+        args[next_arg] = "--force";
+        next_arg++;
+    }
+
+    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[next_arg]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_lvrename:
+ * @vg_name: name of the VG containing the to-be-renamed LV
+ * @lv_name: name of the to-be-renamed LV
+ * @new_name: new name for the @vg_name/@lv_name LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV rename
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully renamed to
+ * @vg_name/@new_name or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[5] = {"lvrename", vg_name, lv_name, new_name, NULL};
+    return call_lvm_and_report_error (args, extra, TRUE, error);
+}
+
+
+/**
+ * bd_lvm_lvresize:
+ * @vg_name: name of the VG containing the to-be-resized LV
+ * @lv_name: name of the to-be-resized LV
+ * @size: the requested new size of the LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully resized or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvresize", "--force", "-L", NULL, NULL, NULL, NULL, NULL};
+    gboolean success = FALSE;
+    guint8 next_arg = 4;
+    g_autofree gchar *lvspec = NULL;
+
+    args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
+
+    /* Starting with 2.03.19 we need to add an extra option to avoid
+       any filesystem related checks by lvresize.
+    */
+    success = bd_utils_check_util_version (deps[DEPS_LVM].name, LVM_VERSION_FSRESIZE,
+                                           deps[DEPS_LVM].ver_arg, deps[DEPS_LVM].ver_regexp, NULL);
+    if (success) {
+      args[next_arg++] = "--fs";
+      args[next_arg++] = "ignore";
+    }
+
+    lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    args[next_arg++] = lvspec;
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[3]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_lvrepair:
+ * @vg_name: name of the VG containing the to-be-repaired LV
+ * @lv_name: name of the to-be-repaired LV
+ * @pv_list: (array zero-terminated=1): list of PVs to be used for the repair
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV repair
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully repaired or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvrepair (const gchar *vg_name, const gchar *lv_name, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
+    guint i = 0;
+    guint pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
+    const gchar **argv = g_new0 (const gchar*, pv_list_len + 5);
+    gboolean success = FALSE;
+
+    argv[0] = "lvconvert";
+    argv[1] = "--repair";
+    argv[2] = "--yes";
+    argv[3] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    for (i=4; i < (pv_list_len + 4); i++) {
+        argv[i] = pv_list[i-4];
+    }
+    argv[i] = NULL;
+
+    success = call_lvm_and_report_error (argv, extra, TRUE, error);
+    g_free ((gchar *) argv[3]);
+    g_free (argv);
+
+    return success;
+}
+
+/**
+ * bd_lvm_lvactivate:
+ * @vg_name: name of the VG containing the to-be-activated LV
+ * @lv_name: name of the to-be-activated LV
+ * @ignore_skip: whether to ignore the skip flag or not
+ * @shared: whether to activate the LV in shared mode (used for shared LVM setups with lvmlockd,
+ *          use %FALSE if not sure)
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV activation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully activated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) {
+    const gchar *args[5] = {"lvchange", NULL, NULL, NULL, NULL};
+    guint8 next_arg = 2;
+    gboolean success = FALSE;
+
+    if (shared)
+        args[1] = "-asy";
+    else
+        args[1] = "-ay";
+
+    if (ignore_skip) {
+        args[next_arg] = "-K";
+        next_arg++;
+    }
+    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[next_arg]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_lvdeactivate:
+ * @vg_name: name of the VG containing the to-be-deactivated LV
+ * @lv_name: name of the to-be-deactivated LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV deactivation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name LV was successfully deactivated or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"lvchange", "-an", NULL, NULL};
+    gboolean success = FALSE;
+
+    args[2] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[2]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_lvsnapshotcreate:
+ * @vg_name: name of the VG containing the LV a new snapshot should be created of
+ * @origin_name: name of the LV a new snapshot should be created of
+ * @snapshot_name: name of the to-be-created snapshot
+ * @size: requested size for the snapshot
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name LV
+ * was successfully created or not.
+ *
+ * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvcreate", "-s", "-L", NULL, "-n", snapshot_name, NULL, NULL};
+    gboolean success = FALSE;
+
+    args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
+    args[6] = g_strdup_printf ("%s/%s", vg_name, origin_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[3]);
+    g_free ((gchar *) args[6]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_lvsnapshotmerge:
+ * @vg_name: name of the VG containing the to-be-merged LV snapshot
+ * @snapshot_name: name of the to-be-merged LV snapshot
+ * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot merge
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@snapshot_name LV snapshot was successfully merged or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[4] = {"lvconvert", "--merge", NULL, NULL};
+    gboolean success = FALSE;
+
+    args[2] = g_strdup_printf ("%s/%s", vg_name, snapshot_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[2]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_add_lv_tags:
+ * @vg_name: name of the VG that contains the LV to set tags on
+ * @lv_name: name of the LV to set tags on
+ * @tags: (array zero-terminated=1): list of tags to add
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully added to @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_add_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
+    g_autofree gchar *lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    return _manage_lvm_tags (lvspec, tags, "--addtag", "lvchange", error);
+}
+
+/**
+ * bd_lvm_delete_lv_tags:
+ * @vg_name: name of the VG that contains the LV to set tags on
+ * @lv_name: name of the LV to set tags on
+ * @tags: (array zero-terminated=1): list of tags to remove
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the tags were successfully removed from @device or not
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+gboolean bd_lvm_delete_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
+    g_autofree gchar *lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
+    return _manage_lvm_tags (lvspec, tags, "--deltag", "lvchange", error);
+}
+
+/**
+ * bd_lvm_lvinfo:
+ * @vg_name: name of the VG that contains the LV to get information about
+ * @lv_name: name of the LV to get information about
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
+ * of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMLVdata* bd_lvm_lvinfo (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--units=b", "-a",
+                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags",
+                       NULL, NULL};
+
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+
+    args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    g_free ((gchar *) args[9]);
+
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 16)) {
+            g_strfreev (lines);
+            return get_lv_data_from_table (table, TRUE);
+        } else
+            if (table)
+                g_hash_table_destroy (table);
+    }
+    g_strfreev (lines);
+
+    /* getting here means no usable info was found */
+    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                 "Failed to parse information about the LV");
+    return NULL;
+}
+
+BDLVMLVdata* bd_lvm_lvinfo_tree (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--units=b", "-a",
+                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags,devices,metadata_devices,seg_size_pe",
+                       NULL, NULL};
+
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+    BDLVMLVdata *result = NULL;
+
+    args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    g_free ((gchar *) args[9]);
+
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 19)) {
+            BDLVMLVdata *lvdata = get_lv_data_from_table (table, TRUE);
+            if (result) {
+                merge_lv_data (result, lvdata);
+                bd_lvm_lvdata_free (lvdata);
+            } else
+                result = lvdata;
+        } else {
+            if (table)
+                g_hash_table_destroy (table);
+        }
+    }
+    g_strfreev (lines);
+
+    if (result == NULL)
+      g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                   "Failed to parse information about the LV");
+    return result;
+}
+
+/**
+ * bd_lvm_lvs:
+ * @vg_name: (nullable): name of the VG to get information about LVs from
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (array zero-terminated=1): information about LVs found in the given
+ * @vg_name VG or in system if @vg_name is %NULL
+ *
+ * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMLVdata** bd_lvm_lvs (const gchar *vg_name, GError **error) {
+    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--units=b", "-a",
+                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags",
+                       NULL, NULL};
+
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+    GPtrArray *lvs;
+    BDLVMLVdata *lvdata = NULL;
+    GError *l_error = NULL;
+
+    lvs = g_ptr_array_new ();
+
+    if (vg_name)
+        args[9] = vg_name;
+
+    success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
+    if (!success) {
+        if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
+            /* no output => no LVs, not an error */
+            g_clear_error (&l_error);
+            /* return an empty list */
+            g_ptr_array_add (lvs, NULL);
+            return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
+        }
+        else {
+            /* the error is already populated from the call */
+            g_ptr_array_free (lvs, TRUE);
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+    }
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 16)) {
+            /* valid line, try to parse and record it */
+            lvdata = get_lv_data_from_table (table, TRUE);
+            if (lvdata) {
+                /* ignore duplicate entries in lvs output, these are caused by multi segments LVs */
+                for (gsize i = 0; i < lvs->len; i++) {
+                    if (g_strcmp0 (((BDLVMLVdata *) g_ptr_array_index (lvs, i))->lv_name, lvdata->lv_name) == 0) {
+                        bd_utils_log_format (BD_UTILS_LOG_DEBUG,
+                                             "Duplicate LV entry for '%s' found in lvs output",
+                                             lvdata->lv_name);
+                        bd_lvm_lvdata_free (lvdata);
+                        lvdata = NULL;
+                        break;
+                    }
+                }
+
+                if (lvdata)
+                    g_ptr_array_add (lvs, lvdata);
+            }
+        } else
+            if (table)
+                g_hash_table_destroy (table);
+    }
+
+    g_strfreev (lines);
+
+    if (lvs->len == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                     "Failed to parse information about LVs");
+        g_ptr_array_free (lvs, TRUE);
+        return NULL;
+    }
+
+    /* returning NULL-terminated array of BDLVMLVdata */
+    g_ptr_array_add (lvs, NULL);
+    return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
+}
+
+BDLVMLVdata** bd_lvm_lvs_tree (const gchar *vg_name, GError **error) {
+    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--units=b", "-a",
+                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags,devices,metadata_devices,seg_size_pe",
+                       NULL, NULL};
+
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+    GPtrArray *lvs;
+    BDLVMLVdata *lvdata = NULL;
+    GError *l_error = NULL;
+
+    lvs = g_ptr_array_new ();
+
+    if (vg_name)
+        args[9] = vg_name;
+
+    success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
+    if (!success) {
+        if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
+            /* no output => no LVs, not an error */
+            g_clear_error (&l_error);
+            /* return an empty list */
+            g_ptr_array_add (lvs, NULL);
+            return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
+        }
+        else {
+            /* the error is already populated from the call */
+            g_ptr_array_free (lvs, TRUE);
+            g_propagate_error (error, l_error);
+            return NULL;
+        }
+    }
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 19)) {
+            /* valid line, try to parse and record it */
+            lvdata = get_lv_data_from_table (table, TRUE);
+            if (lvdata) {
+                for (gsize i = 0; i < lvs->len; i++) {
+                    BDLVMLVdata *other = (BDLVMLVdata *) g_ptr_array_index (lvs, i);
+                    if (g_strcmp0 (other->lv_name, lvdata->lv_name) == 0) {
+                        merge_lv_data (other, lvdata);
+                        bd_lvm_lvdata_free (lvdata);
+                        lvdata = NULL;
+                        break;
+                    }
+                }
+
+                if (lvdata)
+                    g_ptr_array_add (lvs, lvdata);
+            }
+        } else
+            if (table)
+                g_hash_table_destroy (table);
+    }
+
+    g_strfreev (lines);
+
+    if (lvs->len == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                     "Failed to parse information about LVs");
+        g_ptr_array_free (lvs, TRUE);
+        return NULL;
+    }
+
+    /* returning NULL-terminated array of BDLVMLVdata */
+    g_ptr_array_add (lvs, NULL);
+    return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
+}
+
+/**
+ * bd_lvm_thpoolcreate:
+ * @vg_name: name of the VG to create a thin pool in
+ * @lv_name: name of the to-be-created pool LV
+ * @size: requested size of the to-be-created pool
+ * @md_size: requested metadata size or 0 to use the default
+ * @chunk_size: requested chunk size or 0 to use the default
+ * @profile: (nullable): profile to use (see lvm(8) for more information) or %NULL to use
+ *                         the default
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name thin pool was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thpoolcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, guint64 md_size, guint64 chunk_size, const gchar *profile, const BDExtraArg **extra, GError **error) {
+    const gchar *args[9] = {"lvcreate", "-T", "-L", NULL, NULL, NULL, NULL, NULL, NULL};
+    guint8 next_arg = 4;
+    gboolean success = FALSE;
+
+    args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
+
+    if (md_size != 0) {
+        args[next_arg] = g_strdup_printf ("--poolmetadatasize=%"G_GUINT64_FORMAT"K", md_size / 1024);
+        next_arg++;
+    }
+
+    if (chunk_size != 0) {
+        args[next_arg] = g_strdup_printf ("--chunksize=%"G_GUINT64_FORMAT"K", chunk_size / 1024);
+        next_arg++;
+    }
+
+    if (profile) {
+        args[next_arg] = g_strdup_printf ("--profile=%s", profile);
+        next_arg++;
+    }
+
+    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[3]);
+    g_free ((gchar *) args[4]);
+    g_free ((gchar *) args[5]);
+    g_free ((gchar *) args[6]);
+    g_free ((gchar *) args[7]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_thlvcreate:
+ * @vg_name: name of the VG containing the thin pool providing extents for the to-be-created thin LV
+ * @pool_name: name of the pool LV providing extents for the to-be-created thin LV
+ * @lv_name: name of the to-be-created thin LV
+ * @size: requested virtual size of the to-be-created thin LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name thin LV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thlvcreate (const gchar *vg_name, const gchar *pool_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvcreate", "-T", NULL, "-V", NULL, "-n", lv_name, NULL};
+    gboolean success;
+
+    args[2] = g_strdup_printf ("%s/%s", vg_name, pool_name);
+    args[4] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[2]);
+    g_free ((gchar *) args[4]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_thlvpoolname:
+ * @vg_name: name of the VG containing the queried thin LV
+ * @lv_name: name of the queried thin LV
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
+ * thin LV or %NULL if failed to determine (@error) is set in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_thlvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    const gchar *args[6] = {"lvs", "--noheadings", "-o", "pool_lv", NULL, NULL};
+    args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    g_free ((gchar *) args[4]);
+
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    return g_strstrip (output);
+}
+
+/**
+ * bd_lvm_thsnapshotcreate:
+ * @vg_name: name of the VG containing the thin LV a new snapshot should be created of
+ * @origin_name: name of the thin LV a new snapshot should be created of
+ * @snapshot_name: name of the to-be-created snapshot
+ * @pool_name: (nullable): name of the thin pool to create the snapshot in or %NULL if not specified
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV snapshot creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name
+ * thin LV was successfully created or not.
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvcreate", "-s", "-n", snapshot_name, NULL, NULL, NULL, NULL};
+    guint next_arg = 4;
+    gboolean success = FALSE;
+
+    if (pool_name) {
+        args[next_arg] = "--thinpool";
+        next_arg++;
+        args[next_arg] = pool_name;
+        next_arg++;
+    }
+
+    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, origin_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[next_arg]);
+
+    return success;
+}
+
+/**
+ * get_lv_type_from_flags: (skip)
+ * @meta: getting type for a (future) metadata LV
+ *
+ * Get LV type string from flags.
+ */
+static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error G_GNUC_UNUSED) {
+    if (!meta) {
+        if (flags & BD_LVM_CACHE_POOL_STRIPED)
+            return "striped";
+        else if (flags & BD_LVM_CACHE_POOL_RAID1)
+            return "raid1";
+        else if (flags & BD_LVM_CACHE_POOL_RAID5)
+            return "raid5";
+        else if (flags & BD_LVM_CACHE_POOL_RAID6)
+            return "raid6";
+        else if (flags & BD_LVM_CACHE_POOL_RAID10)
+            return "raid10";
+        else
+            return NULL;
+    } else {
+        if (flags & BD_LVM_CACHE_POOL_META_STRIPED)
+            return "striped";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID1)
+            return "raid1";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID5)
+            return "raid5";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID6)
+            return "raid6";
+        else if (flags & BD_LVM_CACHE_POOL_META_RAID10)
+            return "raid10";
+        else
+            return NULL;
+    }
+}
+
+/**
+ * bd_lvm_cache_create_pool:
+ * @vg_name: name of the VG to create @pool_name in
+ * @pool_name: name of the cache pool LV to create
+ * @pool_size: desired size of the cache pool @pool_name
+ * @md_size: desired size of the @pool_name cache pool's metadata LV or 0 to
+ *           use the default
+ * @mode: cache mode of the @pool_name cache pool
+ * @flags: a combination of (ORed) #BDLVMCachePoolFlags
+ * @fast_pvs: (array zero-terminated=1): list of (fast) PVs to create the @pool_name
+ *                                       cache pool (and the metadata LV)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cache pool @vg_name/@pool_name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_cache_create_pool (const gchar *vg_name, const gchar *pool_name, guint64 pool_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags, const gchar **fast_pvs, GError **error) {
+    gboolean success = FALSE;
+    const gchar *type = NULL;
+    gchar *name = NULL;
+    gchar *msg = NULL;
+    guint64 progress_id = 0;
+    const gchar *args[10] = {"lvconvert", "-y", "--type", "cache-pool", "--poolmetadata", NULL, "--cachemode", NULL, NULL, NULL};
+    GError *l_error = NULL;
+
+    msg = g_strdup_printf ("Started 'create cache pool %s/%s'", vg_name, pool_name);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    /* create an LV for the pool */
+    type = get_lv_type_from_flags (flags, FALSE, NULL);
+    success = bd_lvm_lvcreate (vg_name, pool_name, pool_size, type, fast_pvs, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the pool LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 1/3 steps done */
+    bd_utils_report_progress (progress_id, 33, "Created the data LV");
+
+    /* determine the size of the metadata LV */
+    type = get_lv_type_from_flags (flags, TRUE, NULL);
+    if (md_size == 0)
+        md_size = bd_lvm_cache_get_default_md_size (pool_size, &l_error);
+    if (l_error) {
+        g_prefix_error (&l_error, "Failed to determine size for the pool metadata LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+    name = g_strdup_printf ("%s_meta", pool_name);
+
+    /* create the metadata LV */
+    success = bd_lvm_lvcreate (vg_name, name, md_size, type, fast_pvs, NULL, &l_error);
+    if (!success) {
+        g_free (name);
+        g_prefix_error (&l_error, "Failed to create the pool metadata LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 2/3 steps done */
+    bd_utils_report_progress (progress_id, 66, "Created the metadata LV");
+
+    /* create the cache pool from the two LVs */
+    args[5] = name;
+    args[7] = (const gchar *) bd_lvm_cache_get_mode_str (mode, &l_error);
+    if (!args[7]) {
+        g_free ((gchar *) args[5]);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+    name = g_strdup_printf ("%s/%s", vg_name, pool_name);
+    args[8] = name;
+    success = call_lvm_and_report_error (args, NULL, TRUE, &l_error);
+    g_free ((gchar *) args[5]);
+    g_free ((gchar *) args[8]);
+
+    if (!success) {
+        if (l_error)
+            bd_utils_report_finished (progress_id, l_error->message);
+        else
+            bd_utils_report_finished (progress_id, "Completed");
+        g_propagate_error (error, l_error);
+    } else
+        bd_utils_report_finished (progress_id, "Completed");
+
+    /* just return the result of the last step (it sets error on fail) */
+    return success;
+}
+
+/**
+ * bd_lvm_cache_attach:
+ * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
+ * @data_lv: data LV to attach the @cache_pool_lv to
+ * @cache_pool_lv: cache pool LV to attach to the @data_lv
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @cache_pool_lv was successfully attached to the @data_lv or not
+ *
+ * Note: Both @data_lv and @cache_lv will be deactivated before the operation.
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_cache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_pool_lv, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvconvert", "-y", "--type", "cache", "--cachepool", NULL, NULL, NULL};
+    gboolean success = FALSE;
+
+    args[5] = g_strdup_printf ("%s/%s", vg_name, cache_pool_lv);
+    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+
+    g_free ((gchar *) args[5]);
+    g_free ((gchar *) args[6]);
+    return success;
+}
+
+/**
+ * bd_lvm_cache_detach:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: name of the cached LV to detach its cache from
+ * @destroy: whether to destroy the cache after detach or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cache was successfully detached from the @cached_lv or not
+ *
+ * Note: synces the cache first
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_cache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
+    /* need to both "assume yes" and "force" to get rid of the interactive
+       questions in case of "--uncache" */
+    const gchar *args[6] = {"lvconvert", "-y", "-f", NULL, NULL, NULL};
+    gboolean success = FALSE;
+
+    args[3] = destroy ? "--uncache" : "--splitcache";
+    args[4] = g_strdup_printf ("%s/%s", vg_name, cached_lv);
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+
+    g_free ((gchar *) args[4]);
+    return success;
+}
+
+/**
+ * bd_lvm_cache_create_cached_lv:
+ * @vg_name: name of the VG to create a cached LV in
+ * @lv_name: name of the cached LV to create
+ * @data_size: size of the data LV
+ * @cache_size: size of the cache (or cached LV more precisely)
+ * @md_size: size of the cache metadata LV or 0 to use the default
+ * @mode: cache mode for the cached LV
+ * @flags: a combination of (ORed) #BDLVMCachePoolFlags
+ * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
+ * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cached LV @lv_name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_cache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags,
+                                        const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
+    gboolean success = FALSE;
+    gchar *name = NULL;
+    gchar *msg = NULL;
+    guint64 progress_id = 0;
+    GError *l_error = NULL;
+
+    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    name = g_strdup_printf ("%s_cache", lv_name);
+    success = bd_lvm_cache_create_pool (vg_name, name, cache_size, md_size, mode, flags, fast_pvs, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the cache pool '%s': ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 3/5 steps (cache pool creation has 3 steps) done */
+    bd_utils_report_progress (progress_id, 60, "Cache pool created");
+
+    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
+    if (!success) {
+        g_free (name);
+        g_prefix_error (&l_error, "Failed to create the data LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 4/5 steps (cache pool creation has 3 steps) done */
+    bd_utils_report_progress (progress_id, 80, "Data LV created");
+
+    success = bd_lvm_cache_attach (vg_name, lv_name, name, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (error, "Failed to attach the cache pool '%s' to the data LV: ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    bd_utils_report_finished (progress_id, "Completed");
+    g_free (name);
+    return TRUE;
+}
+
+/**
+ * bd_lvm_writecache_attach:
+ * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
+ * @data_lv: data LV to attach the @cache_lv to
+ * @cache_lv: cache (fast) LV to attach to the @data_lv
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @cache_lv was successfully attached to the @data_lv or not
+ *
+ * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_writecache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_lv, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvconvert", "-y", "--type", "writecache", "--cachevol", NULL, NULL, NULL};
+    gboolean success = FALSE;
+
+    /* both LVs need to be inactive for the writecache convert to work */
+    success = bd_lvm_lvdeactivate (vg_name, data_lv, NULL, error);
+    if (!success)
+        return FALSE;
+
+    success = bd_lvm_lvdeactivate (vg_name, cache_lv, NULL, error);
+    if (!success)
+        return FALSE;
+
+    args[5] = g_strdup_printf ("%s/%s", vg_name, cache_lv);
+    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+
+    g_free ((gchar *) args[5]);
+    g_free ((gchar *) args[6]);
+    return success;
+}
+
+/**
+ * bd_lvm_writecache_detach:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: name of the cached LV to detach its cache from
+ * @destroy: whether to destroy the cache after detach or not
+ * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cache was successfully detached from the @cached_lv or not
+ *
+ * Note: synces the cache first
+ *
+ * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_writecache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
+    return bd_lvm_cache_detach (vg_name, cached_lv, destroy, extra, error);
+}
+
+/**
+ * bd_lvm_writecache_create_cached_lv:
+ * @vg_name: name of the VG to create a cached LV in
+ * @lv_name: name of the cached LV to create
+ * @data_size: size of the data LV
+ * @cache_size: size of the cache (or cached LV more precisely)
+ * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
+ * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the cached LV @lv_name was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_writecache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size,
+                                             const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
+    gboolean success = FALSE;
+    gchar *name = NULL;
+    gchar *msg = NULL;
+    guint64 progress_id = 0;
+    GError *l_error = NULL;
+
+    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    name = g_strdup_printf ("%s_writecache", lv_name);
+    success = bd_lvm_lvcreate (vg_name, name, cache_size, NULL, fast_pvs, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to create the cache LV '%s': ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 1/3 steps done */
+    bd_utils_report_progress (progress_id, 33, "Cache LV created");
+
+    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
+    if (!success) {
+        g_free (name);
+        g_prefix_error (&l_error, "Failed to create the data LV: ");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    /* 2/3 steps done */
+    bd_utils_report_progress (progress_id, 66, "Data LV created");
+
+    success = bd_lvm_writecache_attach (vg_name, lv_name, name, NULL, &l_error);
+    if (!success) {
+        g_prefix_error (&l_error, "Failed to attach the cache LV '%s' to the data LV: ", name);
+        g_free (name);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    bd_utils_report_finished (progress_id, "Completed");
+    g_free (name);
+    return TRUE;
+}
+
+/**
+ * bd_lvm_cache_pool_name:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: cached LV to get the name of the its pool LV for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: name of the cache pool LV used by the @cached_lv or %NULL in case of error
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_cache_pool_name (const gchar *vg_name, const gchar *cached_lv, GError **error) {
+    gchar *ret = NULL;
+    gchar *name_start = NULL;
+    gchar *name_end = NULL;
+    gchar *pool_name = NULL;
+
+    /* same as for a thin LV, but with square brackets */
+    ret = bd_lvm_thlvpoolname (vg_name, cached_lv, error);
+    if (!ret)
+        return NULL;
+
+    name_start = strchr (ret, '[');
+    if (!name_start) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Failed to determine cache pool name from: '%s'", ret);
+        g_free (ret);
+        return NULL;
+    }
+    name_start++;
+
+    name_end = strchr (ret, ']');
+    if (!name_end) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Failed to determine cache pool name from: '%s'", ret);
+        g_free (ret);
+        return NULL;
+    }
+
+    pool_name = g_strndup (name_start, name_end - name_start);
+    g_free (ret);
+
+    return pool_name;
+}
+
+/**
+ * bd_lvm_cache_stats:
+ * @vg_name: name of the VG containing the @cached_lv
+ * @cached_lv: cached LV to get stats for
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: stats for the @cached_lv or %NULL in case of error
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error) {
+    struct dm_pool *pool = NULL;
+    struct dm_task *task = NULL;
+    struct dm_info info;
+    struct dm_status_cache *status = NULL;
+    gchar *map_name = NULL;
+    guint64 start = 0;
+    guint64 length = 0;
+    gchar *type = NULL;
+    gchar *params = NULL;
+    BDLVMCacheStats *ret = NULL;
+    BDLVMLVdata *lvdata = NULL;
+
+    if (geteuid () != 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_ROOT,
+                     "Not running as root, cannot query DM maps");
+        return NULL;
+    }
+
+    lvdata = bd_lvm_lvinfo (vg_name, cached_lv, error);
+    if (!lvdata)
+        return NULL;
+
+    pool = dm_pool_create ("bd-pool", 20);
+
+    if (g_strcmp0 (lvdata->segtype, "thin-pool") == 0)
+        map_name = dm_build_dm_name (pool, vg_name, lvdata->data_lv, NULL);
+    else
+        /* translate the VG+LV name into the DM map name */
+        map_name = dm_build_dm_name (pool, vg_name, cached_lv, NULL);
+
+    bd_lvm_lvdata_free (lvdata);
+
+    task = dm_task_create (DM_DEVICE_STATUS);
+    if (!task) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to create DM task for the cache map '%s': ", map_name);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (dm_task_set_name (task, map_name) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to create DM task for the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (dm_task_run (task) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to run the DM task for the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (dm_task_get_info (task, &info) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to get task info for the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    if (!info.exists) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_NOCACHE,
+                     "The cache map '%s' doesn't exist: ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    dm_get_next_target (task, NULL, &start, &length, &type, &params);
+
+    if (dm_get_status_cache (pool, params, &status) == 0) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                     "Failed to get status of the cache map '%s': ", map_name);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        return NULL;
+    }
+
+    ret = g_new0 (BDLVMCacheStats, 1);
+    ret->block_size = status->block_size * SECTOR_SIZE;
+    ret->cache_size = status->total_blocks * ret->block_size;
+    ret->cache_used = status->used_blocks * ret->block_size;
+
+    ret->md_block_size = status->metadata_block_size * SECTOR_SIZE;
+    ret->md_size = status->metadata_total_blocks * ret->md_block_size;
+    ret->md_used = status->metadata_used_blocks * ret->md_block_size;
+
+    ret->read_hits = status->read_hits;
+    ret->read_misses = status->read_misses;
+    ret->write_hits = status->write_hits;
+    ret->write_misses = status->write_misses;
+
+    if (status->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH)
+        ret->mode = BD_LVM_CACHE_MODE_WRITETHROUGH;
+    else if (status->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
+        ret->mode = BD_LVM_CACHE_MODE_WRITEBACK;
+    else {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
+                      "Failed to determine status of the cache from '%"G_GUINT64_FORMAT"': ",
+                      status->feature_flags);
+        dm_task_destroy (task);
+        dm_pool_destroy (pool);
+        bd_lvm_cache_stats_free (ret);
+        return NULL;
+    }
+
+    dm_task_destroy (task);
+    dm_pool_destroy (pool);
+
+    return ret;
+}
+
+/**
+ * bd_lvm_thpool_convert:
+ * @vg_name: name of the VG to create the new thin pool in
+ * @data_lv: name of the LV that should become the data part of the new pool
+ * @metadata_lv: name of the LV that should become the metadata part of the new pool
+ * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Converts the @data_lv and @metadata_lv into a new thin pool in the @vg_name
+ * VG.
+ *
+ * Returns: whether the new thin pool was successfully created from @data_lv and
+ *          @metadata_lv or not
+ *
+ * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_thpool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvconvert", "--yes", "--type", "thin-pool", "--poolmetadata", metadata_lv, NULL, NULL};
+    gboolean success = FALSE;
+
+    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[6]);
+
+    if (success && name)
+        success = bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
+
+    return success;
+}
+
+/**
+ * bd_lvm_cache_pool_convert:
+ * @vg_name: name of the VG to create the new thin pool in
+ * @data_lv: name of the LV that should become the data part of the new pool
+ * @metadata_lv: name of the LV that should become the metadata part of the new pool
+ * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
+ * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Converts the @data_lv and @metadata_lv into a new cache pool in the @vg_name
+ * VG.
+ *
+ * Returns: whether the new cache pool was successfully created from @data_lv and
+ *          @metadata_lv or not
+ *
+ * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_cache_pool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
+    const gchar *args[8] = {"lvconvert", "--yes", "--type", "cache-pool", "--poolmetadata", metadata_lv, NULL, NULL};
+    gboolean success = FALSE;
+
+    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[6]);
+
+    if (success && name)
+        success = bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
+
+    return success;
+}
+
+/**
+ * bd_lvm_vdo_pool_create:
+ * @vg_name: name of the VG to create a new LV in
+ * @lv_name: name of the to-be-created VDO LV
+ * @pool_name: (nullable): name of the to-be-created VDO pool LV or %NULL for default name
+ * @data_size: requested size of the data VDO LV (physical size of the @pool_name VDO pool LV)
+ * @virtual_size: requested virtual_size of the @lv_name VDO LV
+ * @index_memory: amount of index memory (in bytes) or 0 for default
+ * @compression: whether to enable compression or not
+ * @deduplication: whether to enable deduplication or not
+ * @write_policy: write policy for the volume
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the given @vg_name/@lv_name VDO LV was successfully created or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE
+ */
+gboolean bd_lvm_vdo_pool_create (const gchar *vg_name, const gchar *lv_name, const gchar *pool_name, guint64 data_size, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
+    const gchar *args[16] = {"lvcreate", "--type", "vdo", "-n", lv_name, "-L", NULL, "-V", NULL,
+                             "--compression", compression ? "y" : "n",
+                             "--deduplication", deduplication ? "y" : "n",
+                             "-y", NULL, NULL};
+    gboolean success = FALSE;
+    gchar *old_config = NULL;
+    const gchar *write_policy_str = NULL;
+
+    write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
+    if (!write_policy_str)
+        return FALSE;
+
+    args[6] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", data_size / 1024);
+    args[8] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", virtual_size / 1024);
+
+    if (pool_name) {
+        args[14] = g_strdup_printf ("%s/%s", vg_name, pool_name);
+    } else
+        args[14] = vg_name;
+
+    /* index_memory and write_policy can be specified only using the config */
+    g_mutex_lock (&global_config_lock);
+    old_config = global_config_str;
+    if (index_memory != 0)
+        global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
+                                                                                                                                     index_memory / (1024 * 1024),
+                                                                                                                                     write_policy_str);
+    else
+        global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
+                                                                                        write_policy_str);
+
+    success = call_lvm_and_report_error (args, extra, FALSE, error);
+
+    g_free (global_config_str);
+    global_config_str = old_config;
+    g_mutex_unlock (&global_config_lock);
+
+    g_free ((gchar *) args[6]);
+    g_free ((gchar *) args[8]);
+
+    if (pool_name)
+        g_free ((gchar *) args[14]);
+
+    return success;
+}
+
+static gboolean _vdo_set_compression_deduplication (const gchar *vg_name, const gchar *pool_name, const gchar *op, gboolean enable, const BDExtraArg **extra, GError **error) {
+    const gchar *args[5] = {"lvchange", op, enable ? "y" : "n", NULL, NULL};
+    gboolean success = FALSE;
+
+    args[3] = g_strdup_printf ("%s/%s", vg_name, pool_name);
+
+    success = call_lvm_and_report_error (args, extra, TRUE, error);
+    g_free ((gchar *) args[3]);
+
+    return success;
+}
+
+/**
+ * bd_lvm_vdo_enable_compression:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to enable compression on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether compression was successfully enabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_enable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return _vdo_set_compression_deduplication (vg_name, pool_name, "--compression", TRUE, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_disable_compression:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to disable compression on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether compression was successfully disabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_disable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return _vdo_set_compression_deduplication (vg_name, pool_name, "--compression", FALSE, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_enable_deduplication:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to enable deduplication on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether deduplication was successfully enabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_enable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return _vdo_set_compression_deduplication (vg_name, pool_name, "--deduplication", TRUE, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_enable_deduplication:
+ * @vg_name: name of the VG containing the to-be-changed VDO pool LV
+ * @pool_name: name of the VDO pool LV to disable deduplication on
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether deduplication was successfully disabled on @vg_name/@pool_name LV or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_disable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
+    return _vdo_set_compression_deduplication (vg_name, pool_name, "--deduplication", FALSE, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_info:
+ * @vg_name: name of the VG that contains the LV to get information about
+ * @lv_name: name of the LV to get information about
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
+ * of error (the @error) gets populated in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
+ */
+BDLVMVDOPooldata* bd_lvm_vdo_info (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
+                       "--unquoted", "--units=b", "-a",
+                       "-o", "vdo_operating_mode,vdo_compression_state,vdo_index_state,vdo_write_policy,vdo_index_memory_size,vdo_used_size,vdo_saving_percent,vdo_compression,vdo_deduplication",
+                       NULL, NULL};
+
+    GHashTable *table = NULL;
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    gchar **lines = NULL;
+    gchar **lines_p = NULL;
+    guint num_items;
+
+    args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    g_free ((gchar *) args[9]);
+
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    lines = g_strsplit (output, "\n", 0);
+    g_free (output);
+
+    for (lines_p = lines; *lines_p; lines_p++) {
+        table = parse_lvm_vars ((*lines_p), &num_items);
+        if (table && (num_items == 9)) {
+            g_strfreev (lines);
+            return get_vdo_data_from_table (table, TRUE);
+        } else if (table)
+            g_hash_table_destroy (table);
+    }
+    g_strfreev (lines);
+
+    /* getting here means no usable info was found */
+    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
+                 "Failed to parse information about the VDO LV");
+    return NULL;
+}
+
+/**
+ * bd_lvm_vdo_resize:
+ * @vg_name: name of the VG containing the to-be-resized VDO LV
+ * @lv_name: name of the to-be-resized VDO LV
+ * @size: the requested new size of the VDO LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@lv_name VDO LV was successfully resized or not
+ *
+ * Note: Reduction needs to process TRIM for reduced disk area to unmap used data blocks
+ *       from the VDO pool LV and it may take a long time.
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_resize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    return bd_lvm_lvresize (vg_name, lv_name, size, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_pool_resize:
+ * @vg_name: name of the VG containing the to-be-resized VDO pool LV
+ * @pool_name: name of the to-be-resized VDO pool LV
+ * @size: the requested new size of the VDO pool LV
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool LV resize
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether the @vg_name/@pool_name VDO pool LV was successfully resized or not
+ *
+ * Note: Size of the VDO pool LV can be only extended, not reduced.
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_pool_resize (const gchar *vg_name, const gchar *pool_name, guint64 size, const BDExtraArg **extra, GError **error) {
+    BDLVMLVdata *info = NULL;
+
+    info = bd_lvm_lvinfo (vg_name, pool_name, error);
+    if (!info)
+        return FALSE;
+
+    if (info->size >= size) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_SUPPORTED,
+                     "Reducing physical size of the VDO pool LV is not supported.");
+        bd_lvm_lvdata_free (info);
+        return FALSE;
+    }
+
+    bd_lvm_lvdata_free (info);
+
+    return bd_lvm_lvresize (vg_name, pool_name, size, extra, error);
+}
+
+/**
+ * bd_lvm_vdo_pool_convert:
+ * @vg_name: name of the VG that contains @pool_lv
+ * @pool_lv: name of the LV that should become the new VDO pool LV
+ * @name: (nullable): name for the VDO LV or %NULL for default name
+ * @virtual_size: virtual size for the new VDO LV
+ * @index_memory: amount of index memory (in bytes) or 0 for default
+ * @compression: whether to enable compression or not
+ * @deduplication: whether to enable deduplication or not
+ * @write_policy: write policy for the volume
+ * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool creation
+ *                                                 (just passed to LVM as is)
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Converts the @pool_lv into a new VDO pool LV in the @vg_name VG and creates a new
+ * @name VDO LV with size @virtual_size.
+ *
+ * Note: All data on @pool_lv will be irreversibly destroyed.
+ *
+ * Returns: whether the new VDO pool LV was successfully created from @pool_lv and or not
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE&%BD_LVM_TECH_MODE_MODIFY
+ */
+gboolean bd_lvm_vdo_pool_convert (const gchar *vg_name, const gchar *pool_lv, const gchar *name, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
+    const gchar *args[14] = {"lvconvert", "--yes", "--type", "vdo-pool",
+                             "--compression", compression ? "y" : "n",
+                             "--deduplication", deduplication ? "y" : "n",
+                             NULL, NULL, NULL, NULL, NULL, NULL};
+    gboolean success = FALSE;
+    guint next_arg = 4;
+    gchar *size_str = NULL;
+    gchar *lv_spec = NULL;
+    gchar *old_config = NULL;
+    const gchar *write_policy_str = NULL;
+
+    write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
+    if (!write_policy_str)
+        return FALSE;
+
+    if (name) {
+        args[next_arg++] = "-n";
+        args[next_arg++] = name;
+    }
+
+    args[next_arg++] = "-V";
+    size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", virtual_size / 1024);
+    args[next_arg++] = size_str;
+    lv_spec = g_strdup_printf ("%s/%s", vg_name, pool_lv);
+    args[next_arg++] = lv_spec;
+
+    /* index_memory and write_policy can be specified only using the config */
+    g_mutex_lock (&global_config_lock);
+    old_config = global_config_str;
+    if (index_memory != 0)
+        global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
+                                                                                                                                     index_memory / (1024 * 1024),
+                                                                                                                                     write_policy_str);
+    else
+        global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
+                                                                                        write_policy_str);
+
+    success = call_lvm_and_report_error (args, extra, FALSE, error);
+
+    g_free (global_config_str);
+    global_config_str = old_config;
+    g_mutex_unlock (&global_config_lock);
+
+    g_free (size_str);
+    g_free (lv_spec);
+
+    return success;
+}
+
+/**
+ * bd_lvm_vdolvpoolname:
+ * @vg_name: name of the VG containing the queried VDO LV
+ * @lv_name: name of the queried VDO LV
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
+ * VDO LV or %NULL if failed to determine (@error) is set in those cases)
+ *
+ * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
+ */
+gchar* bd_lvm_vdolvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
+    gboolean success = FALSE;
+    gchar *output = NULL;
+    const gchar *args[6] = {"lvs", "--noheadings", "-o", "pool_lv", NULL, NULL};
+    args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
+
+    success = call_lvm_and_capture_output (args, NULL, &output, error);
+    g_free ((gchar *) args[4]);
+
+    if (!success)
+        /* the error is already populated from the call */
+        return NULL;
+
+    return g_strstrip (output);
+}
diff -pruN 3.3.1-3/src/plugins/lvm/lvm.h 3.4.0-1/src/plugins/lvm/lvm.h
--- 3.3.1-3/src/plugins/lvm/lvm.h	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/lvm.h	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,339 @@
+#include <glib.h>
+#include <blockdev/utils.h>
+
+#ifndef BD_LVM
+#define BD_LVM
+
+GQuark bd_lvm_error_quark (void);
+#define BD_LVM_ERROR bd_lvm_error_quark ()
+typedef enum {
+    BD_LVM_ERROR_TECH_UNAVAIL,
+    BD_LVM_ERROR_FAIL,
+    BD_LVM_ERROR_PARSE,
+    BD_LVM_ERROR_NOEXIST,
+    BD_LVM_ERROR_DM_ERROR,
+    BD_LVM_ERROR_NOT_ROOT,
+    BD_LVM_ERROR_CACHE_INVAL,
+    BD_LVM_ERROR_CACHE_NOCACHE,
+    BD_LVM_ERROR_NOT_SUPPORTED,
+    BD_LVM_ERROR_VDO_POLICY_INVAL,
+    BD_LVM_ERROR_DEVICES_DISABLED,
+} BDLVMError;
+
+typedef enum {
+    BD_LVM_CACHE_POOL_STRIPED =  1 << 0,
+    BD_LVM_CACHE_POOL_RAID1 =    1 << 1,
+    BD_LVM_CACHE_POOL_RAID5 =    1 << 2,
+    BD_LVM_CACHE_POOL_RAID6 =    1 << 3,
+    BD_LVM_CACHE_POOL_RAID10 =   1 << 4,
+
+    BD_LVM_CACHE_POOL_META_STRIPED =  1 << 10,
+    BD_LVM_CACHE_POOL_META_RAID1 =    1 << 11,
+    BD_LVM_CACHE_POOL_META_RAID5 =    1 << 12,
+    BD_LVM_CACHE_POOL_META_RAID6 =    1 << 13,
+    BD_LVM_CACHE_POOL_META_RAID10 =   1 << 14,
+} BDLVMCachePoolFlags;
+
+typedef enum {
+    BD_LVM_CACHE_MODE_UNKNOWN,
+    BD_LVM_CACHE_MODE_WRITETHROUGH,
+    BD_LVM_CACHE_MODE_WRITEBACK,
+} BDLVMCacheMode;
+
+typedef enum {
+    BD_LVM_VDO_MODE_UNKNOWN,
+    BD_LVM_VDO_MODE_RECOVERING,
+    BD_LVM_VDO_MODE_READ_ONLY,
+    BD_LVM_VDO_MODE_NORMAL,
+} BDLVMVDOOperatingMode;
+
+typedef enum {
+    BD_LVM_VDO_COMPRESSION_UNKNOWN,
+    BD_LVM_VDO_COMPRESSION_ONLINE,
+    BD_LVM_VDO_COMPRESSION_OFFLINE,
+} BDLVMVDOCompressionState;
+
+typedef enum {
+    BD_LVM_VDO_INDEX_UNKNOWN,
+    BD_LVM_VDO_INDEX_ERROR,
+    BD_LVM_VDO_INDEX_CLOSED,
+    BD_LVM_VDO_INDEX_OPENING,
+    BD_LVM_VDO_INDEX_CLOSING,
+    BD_LVM_VDO_INDEX_OFFLINE,
+    BD_LVM_VDO_INDEX_ONLINE,
+} BDLVMVDOIndexState;
+
+typedef enum {
+    BD_LVM_VDO_WRITE_POLICY_UNKNOWN,
+    BD_LVM_VDO_WRITE_POLICY_AUTO,
+    BD_LVM_VDO_WRITE_POLICY_SYNC,
+    BD_LVM_VDO_WRITE_POLICY_ASYNC,
+} BDLVMVDOWritePolicy;
+
+typedef struct BDLVMPVdata {
+    gchar *pv_name;
+    gchar *pv_uuid;
+    guint64 pv_free;
+    guint64 pv_size;
+    guint64 pe_start;
+    gchar *vg_name;
+    gchar *vg_uuid;
+    guint64 vg_size;
+    guint64 vg_free;
+    guint64 vg_extent_size;
+    guint64 vg_extent_count;
+    guint64 vg_free_count;
+    guint64 vg_pv_count;
+    gchar **pv_tags;
+    gboolean missing;
+} BDLVMPVdata;
+
+void bd_lvm_pvdata_free (BDLVMPVdata *data);
+BDLVMPVdata* bd_lvm_pvdata_copy (BDLVMPVdata *data);
+
+typedef struct BDLVMVGdata {
+    gchar *name;
+    gchar *uuid;
+    guint64 size;
+    guint64 free;
+    guint64 extent_size;
+    guint64 extent_count;
+    guint64 free_count;
+    guint64 pv_count;
+    gboolean exported;
+    gchar **vg_tags;
+} BDLVMVGdata;
+
+void bd_lvm_vgdata_free (BDLVMVGdata *data);
+BDLVMVGdata* bd_lvm_vgdata_copy (BDLVMVGdata *data);
+
+typedef struct BDLVMSEGdata {
+    guint64 size_pe;
+    guint64 pv_start_pe;
+    gchar *pvdev;
+} BDLVMSEGdata;
+
+BDLVMSEGdata* bd_lvm_segdata_copy (BDLVMSEGdata *data);
+void bd_lvm_segdata_free (BDLVMSEGdata *data);
+
+typedef struct BDLVMLVdata {
+    gchar *lv_name;
+    gchar *vg_name;
+    gchar *uuid;
+    guint64 size;
+    gchar *attr;
+    gchar *segtype;
+    gchar *origin;
+    gchar *pool_lv;
+    gchar *data_lv;
+    gchar *metadata_lv;
+    gchar *roles;
+    gchar *move_pv;
+    guint64 data_percent;
+    guint64 metadata_percent;
+    guint64 copy_percent;
+    gchar **lv_tags;
+    gchar **data_lvs;
+    gchar **metadata_lvs;
+    BDLVMSEGdata **segs;
+} BDLVMLVdata;
+
+void bd_lvm_lvdata_free (BDLVMLVdata *data);
+BDLVMLVdata* bd_lvm_lvdata_copy (BDLVMLVdata *data);
+
+typedef struct BDLVMVDOPooldata {
+    BDLVMVDOOperatingMode operating_mode;
+    BDLVMVDOCompressionState compression_state;
+    BDLVMVDOIndexState index_state;
+    BDLVMVDOWritePolicy write_policy;
+    guint64 used_size;
+    gint32 saving_percent;
+    guint64 index_memory_size;
+    gboolean deduplication;
+    gboolean compression;
+} BDLVMVDOPooldata;
+
+void bd_lvm_vdopooldata_free (BDLVMVDOPooldata *data);
+BDLVMVDOPooldata* bd_lvm_vdopooldata_copy (BDLVMVDOPooldata *data);
+
+typedef struct BDLVMVDOStats {
+    gint64 block_size;
+    gint64 logical_block_size;
+    gint64 physical_blocks;
+    gint64 data_blocks_used;
+    gint64 overhead_blocks_used;
+    gint64 logical_blocks_used;
+    gint64 used_percent;
+    gint64 saving_percent;
+    gdouble write_amplification_ratio;
+} BDLVMVDOStats;
+
+void bd_lvm_vdo_stats_free (BDLVMVDOStats *stats);
+BDLVMVDOStats* bd_lvm_vdo_stats_copy (BDLVMVDOStats *stats);
+
+typedef struct BDLVMCacheStats {
+    guint64 block_size;
+    guint64 cache_size;
+    guint64 cache_used;
+    guint64 md_block_size;
+    guint64 md_size;
+    guint64 md_used;
+    guint64 read_hits;
+    guint64 read_misses;
+    guint64 write_hits;
+    guint64 write_misses;
+    BDLVMCacheMode mode;
+} BDLVMCacheStats;
+
+void bd_lvm_cache_stats_free (BDLVMCacheStats *data);
+BDLVMCacheStats* bd_lvm_cache_stats_copy (BDLVMCacheStats *data);
+
+typedef enum {
+    BD_LVM_TECH_BASIC = 0,
+    BD_LVM_TECH_BASIC_SNAP,
+    BD_LVM_TECH_THIN,
+    BD_LVM_TECH_CACHE,
+    BD_LVM_TECH_CALCS,
+    BD_LVM_TECH_THIN_CALCS,
+    BD_LVM_TECH_CACHE_CALCS,
+    BD_LVM_TECH_GLOB_CONF,
+    BD_LVM_TECH_VDO,
+    BD_LVM_TECH_WRITECACHE,
+    BD_LVM_TECH_DEVICES,
+    BD_LVM_TECH_SHARED,
+    BD_LVM_TECH_CONFIG,
+    BD_LVM_TECH_VG_CFG_BACKUP_RESTORE,
+} BDLVMTech;
+
+typedef enum {
+    BD_LVM_TECH_MODE_CREATE = 1 << 0,
+    BD_LVM_TECH_MODE_REMOVE = 1 << 2,
+    BD_LVM_TECH_MODE_MODIFY = 1 << 3,
+    BD_LVM_TECH_MODE_QUERY  = 1 << 4,
+} BDLVMTechMode;
+
+
+/*
+ * If using the plugin as a standalone library, the following functions should
+ * be called to:
+ *
+ * init()       - initialize the plugin, returning TRUE on success
+ * close()      - clean after the plugin at the end or if no longer used
+ *
+ */
+gboolean bd_lvm_init (void);
+void bd_lvm_close (void);
+
+gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error);
+
+gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error);
+guint64 *bd_lvm_get_supported_pe_sizes (GError **error);
+guint64 bd_lvm_get_max_lv_size (GError **error);
+guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error);
+guint64 bd_lvm_get_lv_physical_size (guint64 lv_size, guint64 pe_size, GError **error);
+guint64 bd_lvm_get_thpool_padding (guint64 size, guint64 pe_size, gboolean included, GError **error);
+guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots, GError **error);
+gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error);
+gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error);
+
+gboolean bd_lvm_pvcreate (const gchar *device, guint64 data_alignment, guint64 metadata_size, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_pvremove (const gchar *device, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_pvmove (const gchar *src, const gchar *dest, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_pvscan (const gchar *device, gboolean update_cache, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_add_pv_tags (const gchar *device, const gchar **tags, GError **error);
+gboolean bd_lvm_delete_pv_tags (const gchar *device, const gchar **tags, GError **error);
+BDLVMPVdata* bd_lvm_pvinfo (const gchar *device, GError **error);
+BDLVMPVdata** bd_lvm_pvs (GError **error);
+
+gboolean bd_lvm_vgcreate (const gchar *name, const gchar **pv_list, guint64 pe_size, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgremove (const gchar *vg_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgrename (const gchar *old_vg_name, const gchar *new_vg_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError **error);
+gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error);
+gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgcfgbackup (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vgcfgrestore (const gchar *vg_name, const gchar *backup_file, const BDExtraArg **extra, GError **error);
+BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error);
+BDLVMVGdata** bd_lvm_vgs (GError **error);
+
+gchar* bd_lvm_lvorigin (const gchar *vg_name, const gchar *lv_name, GError **error);
+gboolean bd_lvm_lvcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, const gchar *type, const gchar **pv_list, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean force, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvrepair (const gchar *vg_name, const gchar *lv_name, const gchar **pv_list, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_add_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error);
+gboolean bd_lvm_delete_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error);
+BDLVMLVdata* bd_lvm_lvinfo (const gchar *vg_name, const gchar *lv_name, GError **error);
+BDLVMLVdata* bd_lvm_lvinfo_tree (const gchar *vg_name, const gchar *lv_name, GError **error);
+BDLVMLVdata** bd_lvm_lvs (const gchar *vg_name, GError **error);
+BDLVMLVdata** bd_lvm_lvs_tree (const gchar *vg_name, GError **error);
+
+gboolean bd_lvm_thpoolcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, guint64 md_size, guint64 chunk_size, const gchar *profile, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_thlvcreate (const gchar *vg_name, const gchar *pool_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error);
+gchar* bd_lvm_thlvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error);
+gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
+
+gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error);
+gchar* bd_lvm_get_global_config (GError **error);
+
+gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error);
+gchar** bd_lvm_get_devices_filter (GError **error);
+
+guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error);
+const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error);
+BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error);
+gboolean bd_lvm_cache_create_pool (const gchar *vg_name, const gchar *pool_name, guint64 pool_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags, const gchar **fast_pvs, GError **error);
+gboolean bd_lvm_cache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_pool_lv, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_cache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_cache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags,
+                                        const gchar **slow_pvs, const gchar **fast_pvs, GError **error);
+gchar* bd_lvm_cache_pool_name (const gchar *vg_name, const gchar *cached_lv, GError **error);
+BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error);
+
+gboolean bd_lvm_writecache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_lv, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_writecache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_writecache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, const gchar **slow_pvs, const gchar **fast_pvs, GError **error);
+
+gboolean bd_lvm_vdo_pool_create (const gchar *vg_name, const gchar *lv_name, const gchar *pool_name, guint64 data_size, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error);
+BDLVMVDOPooldata *bd_lvm_vdo_info (const gchar *vg_name, const gchar *lv_name, GError **error);
+
+gboolean bd_lvm_vdo_resize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vdo_pool_resize (const gchar *vg_name, const gchar *pool_name, guint64 size, const BDExtraArg **extra, GError **error);
+
+gboolean bd_lvm_vdo_enable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vdo_disable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vdo_enable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vdo_disable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
+
+gboolean bd_lvm_thpool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_cache_pool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_vdo_pool_convert (const gchar *vg_name, const gchar *pool_lv, const gchar *name, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error);
+gchar* bd_lvm_vdolvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error);
+
+const gchar* bd_lvm_get_vdo_operating_mode_str (BDLVMVDOOperatingMode mode, GError **error);
+const gchar* bd_lvm_get_vdo_compression_state_str (BDLVMVDOCompressionState state, GError **error);
+const gchar* bd_lvm_get_vdo_index_state_str (BDLVMVDOIndexState state, GError **error);
+const gchar* bd_lvm_get_vdo_write_policy_str (BDLVMVDOWritePolicy policy, GError **error);
+
+BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_str, GError **error);
+
+BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error);
+GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error);
+
+gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
+gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
+
+gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error);
+
+#endif /* BD_LVM */
diff -pruN 3.3.1-3/src/plugins/lvm/vdo_stats.c 3.4.0-1/src/plugins/lvm/vdo_stats.c
--- 3.3.1-3/src/plugins/lvm/vdo_stats.c	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/vdo_stats.c	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2020  Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vojtech Trefny <vtrefny@redhat.com>
+ */
+
+#include <glib.h>
+#include <blockdev/utils.h>
+#include <libdevmapper.h>
+#include <yaml.h>
+
+#include "vdo_stats.h"
+#include "lvm.h"
+
+
+G_GNUC_INTERNAL gboolean
+get_stat_val64 (GHashTable *stats, const gchar *key, gint64 *val) {
+    const gchar *s;
+    gchar *endptr = NULL;
+
+    s = g_hash_table_lookup (stats, key);
+    if (s == NULL)
+        return FALSE;
+
+    *val = g_ascii_strtoll (s, &endptr, 0);
+    if (endptr == NULL || *endptr != '\0')
+        return FALSE;
+
+    return TRUE;
+}
+
+G_GNUC_INTERNAL gboolean
+get_stat_val64_default (GHashTable *stats, const gchar *key, gint64 *val, gint64 def) {
+    if (!get_stat_val64 (stats, key, val))
+        *val = def;
+    return TRUE;
+}
+
+G_GNUC_INTERNAL gboolean
+get_stat_val_double (GHashTable *stats, const gchar *key, gdouble *val) {
+    const gchar *s;
+    gchar *endptr = NULL;
+
+    s = g_hash_table_lookup (stats, key);
+    if (s == NULL)
+        return FALSE;
+
+    *val = g_ascii_strtod (s, &endptr);
+    if (endptr == NULL || *endptr != '\0')
+        return FALSE;
+
+    return TRUE;
+}
+
+static void add_write_ampl_r_stats (GHashTable *stats) {
+    gint64 bios_meta_write, bios_out_write, bios_in_write;
+
+    if (! get_stat_val64 (stats, "biosMetaWrite", &bios_meta_write) ||
+        ! get_stat_val64 (stats, "biosOutWrite", &bios_out_write) ||
+        ! get_stat_val64 (stats, "biosInWrite", &bios_in_write))
+        return;
+
+    if (bios_in_write <= 0)
+        g_hash_table_replace (stats, g_strdup ("writeAmplificationRatio"), g_strdup ("0.00"));
+    else
+        g_hash_table_replace (stats,
+                              g_strdup ("writeAmplificationRatio"),
+                              g_strdup_printf ("%.2f", (gfloat) (bios_meta_write + bios_out_write) / (gfloat) bios_in_write));
+}
+
+static void add_block_stats (GHashTable *stats) {
+    gint64 physical_blocks, block_size, data_blocks_used, overhead_blocks_used, logical_blocks_used;
+    gint64 savings;
+
+    if (! get_stat_val64 (stats, "physicalBlocks", &physical_blocks) ||
+        ! get_stat_val64 (stats, "blockSize", &block_size) ||
+        ! get_stat_val64 (stats, "dataBlocksUsed", &data_blocks_used) ||
+        ! get_stat_val64 (stats, "overheadBlocksUsed", &overhead_blocks_used) ||
+        ! get_stat_val64 (stats, "logicalBlocksUsed", &logical_blocks_used))
+        return;
+
+    g_hash_table_replace (stats, g_strdup ("oneKBlocks"), g_strdup_printf ("%"G_GINT64_FORMAT, physical_blocks * block_size / 1024));
+    g_hash_table_replace (stats, g_strdup ("oneKBlocksUsed"), g_strdup_printf ("%"G_GINT64_FORMAT, (data_blocks_used + overhead_blocks_used) * block_size / 1024));
+    g_hash_table_replace (stats, g_strdup ("oneKBlocksAvailable"), g_strdup_printf ("%"G_GINT64_FORMAT, (physical_blocks - data_blocks_used - overhead_blocks_used) * block_size / 1024));
+    g_hash_table_replace (stats, g_strdup ("usedPercent"), g_strdup_printf ("%.0f", 100.0 * (gfloat) (data_blocks_used + overhead_blocks_used) / (gfloat) physical_blocks + 0.5));
+    savings = (logical_blocks_used > 0) ? (gint64) (100.0 * (gfloat) (logical_blocks_used - data_blocks_used) / (gfloat) logical_blocks_used) : 100;
+    g_hash_table_replace (stats, g_strdup ("savings"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
+    if (savings >= 0)
+        g_hash_table_replace (stats, g_strdup ("savingPercent"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
+}
+
+static void add_journal_stats (GHashTable *stats) {
+    gint64 journal_entries_committed, journal_entries_started, journal_entries_written;
+    gint64 journal_blocks_committed, journal_blocks_started, journal_blocks_written;
+
+    if (! get_stat_val64 (stats, "journalEntriesCommitted", &journal_entries_committed) ||
+        ! get_stat_val64 (stats, "journalEntriesStarted", &journal_entries_started) ||
+        ! get_stat_val64 (stats, "journalEntriesWritten", &journal_entries_written) ||
+        ! get_stat_val64 (stats, "journalBlocksCommitted", &journal_blocks_committed) ||
+        ! get_stat_val64 (stats, "journalBlocksStarted", &journal_blocks_started) ||
+        ! get_stat_val64 (stats, "journalBlocksWritten", &journal_blocks_written))
+        return;
+
+    g_hash_table_replace (stats, g_strdup ("journalEntriesBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_started - journal_entries_written));
+    g_hash_table_replace (stats, g_strdup ("journalEntriesWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_written - journal_entries_committed));
+    g_hash_table_replace (stats, g_strdup ("journalBlocksBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_started - journal_blocks_written));
+    g_hash_table_replace (stats, g_strdup ("journalBlocksWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_written - journal_blocks_committed));
+}
+
+static void add_computed_stats (GHashTable *stats) {
+    const gchar *s;
+
+    s = g_hash_table_lookup (stats, "logicalBlockSize");
+    g_hash_table_replace (stats,
+                          g_strdup ("fiveTwelveByteEmulation"),
+                          g_strdup ((g_strcmp0 (s, "512") == 0) ? "true" : "false"));
+
+    add_write_ampl_r_stats (stats);
+    add_block_stats (stats);
+    add_journal_stats (stats);
+}
+
+enum parse_flags {
+  PARSE_NEXT_KEY,
+  PARSE_NEXT_VAL,
+  PARSE_NEXT_IGN,
+};
+
+G_GNUC_INTERNAL GHashTable *
+vdo_get_stats_full (const gchar *name, GError **error) {
+    struct dm_task *dmt = NULL;
+    const gchar *response = NULL;
+    yaml_parser_t parser;
+    yaml_token_t token;
+    GHashTable *stats = NULL;
+    gchar *key = NULL;
+    gsize len = 0;
+    int next_token = PARSE_NEXT_IGN;
+    gchar *prefix = NULL;
+
+    dmt = dm_task_create (DM_DEVICE_TARGET_MSG);
+    if (!dmt) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to create DM task");
+        return NULL;
+    }
+
+    if (!dm_task_set_name (dmt, name)) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to set name for DM task");
+        dm_task_destroy (dmt);
+        return NULL;
+    }
+
+    if (!dm_task_set_message (dmt, "stats")) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to set message for DM task");
+        dm_task_destroy (dmt);
+        return NULL;
+    }
+
+    if (!dm_task_run (dmt)) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to run DM task");
+        dm_task_destroy (dmt);
+        return NULL;
+    }
+
+    response = dm_task_get_message_response (dmt);
+    if (!response) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to get response from the DM task");
+        dm_task_destroy (dmt);
+        return NULL;
+    }
+
+    if (!yaml_parser_initialize (&parser)) {
+        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
+                     "Failed to get initialize YAML parser");
+        dm_task_destroy (dmt);
+        return NULL;
+    }
+
+    stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    yaml_parser_set_input_string (&parser, (guchar *) response, strlen (response));
+
+    do {
+        yaml_parser_scan (&parser, &token);
+        switch (token.type) {
+            /* key */
+            case YAML_KEY_TOKEN:
+                next_token = PARSE_NEXT_KEY;
+                break;
+            /* value */
+            case YAML_VALUE_TOKEN:
+                next_token = PARSE_NEXT_VAL;
+                break;
+            /* block mapping */
+            case YAML_BLOCK_MAPPING_START_TOKEN:
+                if (next_token == PARSE_NEXT_VAL)
+                    /* we were expecting to read a key-value pair but this is actually
+                       a block start, so we need to free the key we're not going to use */
+                    g_free (key);
+                break;
+            /* mapping */
+            case YAML_FLOW_MAPPING_START_TOKEN:
+                /* start of flow mapping -> previously read key will be used as prefix
+                   for all keys in the mapping:
+                        previous key: biosInProgress
+                        keys in the mapping: Read, Write...
+                        with prefix: biosInProgressRead, biosInProgressWrite...
+                */
+                prefix = key;
+                break;
+            case YAML_FLOW_MAPPING_END_TOKEN:
+                /* end of flow mapping, discard the prefix used */
+                g_free (prefix);
+                prefix = NULL;
+                break;
+            /* actual data */
+            case YAML_SCALAR_TOKEN:
+                if (next_token == PARSE_NEXT_KEY) {
+                    if (prefix) {
+                        key = g_strdup_printf ("%s%s", prefix, (const gchar *) token.data.scalar.value);
+                        len = strlen (prefix);
+                        /* make sure the key with the prefix is still camelCase */
+                        key[len] = g_ascii_toupper (key[len]);
+                    } else
+                        key = g_strdup ((const gchar *) token.data.scalar.value);
+                } else if (next_token == PARSE_NEXT_VAL) {
+                    gchar *val = g_strdup ((const gchar *) token.data.scalar.value);
+                    g_hash_table_insert (stats, key, val);
+                }
+                break;
+            default:
+                break;
+          }
+
+          if (token.type != YAML_STREAM_END_TOKEN)
+              yaml_token_delete (&token);
+    } while (token.type != YAML_STREAM_END_TOKEN);
+
+    yaml_parser_delete (&parser);
+    dm_task_destroy (dmt);
+
+    if (stats != NULL)
+        add_computed_stats (stats);
+
+    return stats;
+}
diff -pruN 3.3.1-3/src/plugins/lvm/vdo_stats.h 3.4.0-1/src/plugins/lvm/vdo_stats.h
--- 3.3.1-3/src/plugins/lvm/vdo_stats.h	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm/vdo_stats.h	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vojtech Trefny <vtrefny@redhat.com>
+ */
+
+#include <glib.h>
+
+#ifndef BD_VDO_STATS
+#define BD_VDO_STATS
+
+gboolean get_stat_val_double (GHashTable *stats, const gchar *key, gdouble *val);
+gboolean get_stat_val64 (GHashTable *stats, const gchar *key, gint64 *val);
+gboolean get_stat_val64_default (GHashTable *stats, const gchar *key, gint64 *val, gint64 def);
+
+GHashTable* vdo_get_stats_full (const gchar *name, GError **error);
+
+#endif  /* BD_VDO_STATS */
diff -pruN 3.3.1-3/src/plugins/lvm-dbus.c 3.4.0-1/src/plugins/lvm-dbus.c
--- 3.3.1-3/src/plugins/lvm-dbus.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm-dbus.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,4979 +0,0 @@
-/*
- * Copyright (C) 2015-2016  Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Vratislav Podzimek <vpodzime@redhat.com>
- */
-
-#include <glib.h>
-#include <math.h>
-#include <string.h>
-#include <unistd.h>
-#include <blockdev/utils.h>
-#include <gio/gio.h>
-#include <libdevmapper.h>
-
-#include "lvm.h"
-#include "check_deps.h"
-#include "dm_logging.h"
-#include "vdo_stats.h"
-
-#define INT_FLOAT_EPS 1e-5
-#define SECTOR_SIZE 512
-#define VDO_POOL_SUFFIX "vpool"
-#define DEFAULT_PE_SIZE (4 MiB)
-#define USE_DEFAULT_PE_SIZE 0
-#define RESOLVE_PE_SIZE(size) ((size) == USE_DEFAULT_PE_SIZE ? DEFAULT_PE_SIZE : (size))
-#define THPOOL_MD_FACTOR_NEW (0.2)
-#define THPOOL_MD_FACTOR_EXISTS (1 / 6.0)
-
-#define MIN_PE_SIZE (1 KiB)
-#define MAX_PE_SIZE (16 GiB)
-
-#define MIN_THPOOL_MD_SIZE (4 MiB)
-/* DM_THIN_MAX_METADATA_SIZE is in 512 sectors */
-#define MAX_THPOOL_MD_SIZE (DM_THIN_MAX_METADATA_SIZE * 512)
-
-#define MIN_THPOOL_CHUNK_SIZE (64 KiB)
-#define MAX_THPOOL_CHUNK_SIZE (1 GiB)
-#define DEFAULT_CHUNK_SIZE (64 KiB)
-
-/* according to lvmcache (7) */
-#define MIN_CACHE_MD_SIZE (8 MiB)
-
-#define LVM_MIN_VERSION "2.02.116"
-#define LVM_VERSION_FSRESIZE "2.03.19"
-
-#ifdef __LP64__
-/* 64bit system */
-#define MAX_LV_SIZE (8 EiB)
-#else
-/* 32bit system */
-#define MAX_LV_SIZE (16 TiB)
-#endif
-
-static GMutex global_config_lock;
-static gchar *global_config_str = NULL;
-
-static gchar *global_devices_str = NULL;
-
-#define LVM_BUS_NAME "com.redhat.lvmdbus1"
-#define LVM_OBJ_PREFIX "/com/redhat/lvmdbus1"
-#define MANAGER_OBJ "/com/redhat/lvmdbus1/Manager"
-#define MANAGER_INTF "com.redhat.lvmdbus1.Manager"
-#define JOB_OBJ_PREFIX "/com/redhat/lvmdbus1/Job/"
-#define JOB_INTF "com.redhat.lvmdbus1.Job"
-#define PV_OBJ_PREFIX LVM_OBJ_PREFIX"/Pv"
-#define VG_OBJ_PREFIX LVM_OBJ_PREFIX"/Vg"
-#define LV_OBJ_PREFIX LVM_OBJ_PREFIX"/Lv"
-#define HIDDEN_LV_OBJ_PREFIX LVM_OBJ_PREFIX"/HiddenLv"
-#define THIN_POOL_OBJ_PREFIX LVM_OBJ_PREFIX"/ThinPool"
-#define CACHE_POOL_OBJ_PREFIX LVM_OBJ_PREFIX"/CachePool"
-#define VDO_POOL_OBJ_PREFIX LVM_OBJ_PREFIX"/VdoPool"
-#define PV_INTF LVM_BUS_NAME".Pv"
-#define VG_INTF LVM_BUS_NAME".Vg"
-#define VG_VDO_INTF LVM_BUS_NAME".VgVdo"
-#define LV_CMN_INTF LVM_BUS_NAME".LvCommon"
-#define LV_INTF LVM_BUS_NAME".Lv"
-#define CACHED_LV_INTF LVM_BUS_NAME".CachedLv"
-#define SNAP_INTF LVM_BUS_NAME".Snapshot"
-#define THPOOL_INTF LVM_BUS_NAME".ThinPool"
-#define CACHE_POOL_INTF LVM_BUS_NAME".CachePool"
-#define VDO_POOL_INTF LVM_BUS_NAME".VdoPool"
-#define DBUS_PROPS_IFACE "org.freedesktop.DBus.Properties"
-#define DBUS_INTRO_IFACE "org.freedesktop.DBus.Introspectable"
-#define METHOD_CALL_TIMEOUT 5000
-#define PROGRESS_WAIT 500 * 1000 /* microseconds */
-
-
-static GDBusConnection *bus = NULL;
-
-/**
- * SECTION: lvm
- * @short_description: plugin for operations with LVM
- * @title: LVM
- * @include: lvm.h
- *
- * A plugin for operations with LVM. All sizes passed in/out to/from
- * the functions are in bytes.
- */
-
-/**
- * bd_lvm_error_quark: (skip)
- */
-GQuark bd_lvm_error_quark (void)
-{
-    return g_quark_from_static_string ("g-bd-lvm-error-quark");
-}
-
-BDLVMPVdata* bd_lvm_pvdata_copy (BDLVMPVdata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMPVdata *new_data = g_new0 (BDLVMPVdata, 1);
-
-    new_data->pv_name = g_strdup (data->pv_name);
-    new_data->pv_uuid = g_strdup (data->pv_uuid);
-    new_data->pv_free = data->pv_free;
-    new_data->pv_size = data->pv_size;
-    new_data->pe_start = data->pe_start;
-    new_data->vg_name = g_strdup (data->vg_name);
-    new_data->vg_uuid = g_strdup (data->vg_uuid);
-    new_data->vg_size = data->vg_size;
-    new_data->vg_free = data->vg_free;
-    new_data->vg_extent_size = data->vg_extent_size;
-    new_data->vg_extent_count = data->vg_extent_count;
-    new_data->vg_free_count = data->vg_free_count;
-    new_data->vg_pv_count = data->vg_pv_count;
-    new_data->pv_tags = g_strdupv (data->pv_tags);
-
-    return new_data;
-}
-
-void bd_lvm_pvdata_free (BDLVMPVdata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data->pv_name);
-    g_free (data->pv_uuid);
-    g_free (data->vg_name);
-    g_free (data->vg_uuid);
-    g_strfreev (data->pv_tags);
-    g_free (data);
-}
-
-BDLVMVGdata* bd_lvm_vgdata_copy (BDLVMVGdata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMVGdata *new_data = g_new0 (BDLVMVGdata, 1);
-
-    new_data->name = g_strdup (data->name);
-    new_data->uuid = g_strdup (data->uuid);
-    new_data->size = data->size;
-    new_data->free = data->free;
-    new_data->extent_size = data->extent_size;
-    new_data->extent_count = data->extent_count;
-    new_data->free_count = data->free_count;
-    new_data->pv_count = data->pv_count;
-    new_data->vg_tags = g_strdupv (data->vg_tags);
-    return new_data;
-}
-
-void bd_lvm_vgdata_free (BDLVMVGdata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data->name);
-    g_free (data->uuid);
-    g_strfreev (data->vg_tags);
-    g_free (data);
-}
-
-BDLVMLVdata* bd_lvm_lvdata_copy (BDLVMLVdata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMLVdata *new_data = g_new0 (BDLVMLVdata, 1);
-
-    new_data->lv_name = g_strdup (data->lv_name);
-    new_data->vg_name = g_strdup (data->vg_name);
-    new_data->uuid = g_strdup (data->uuid);
-    new_data->size = data->size;
-    new_data->attr = g_strdup (data->attr);
-    new_data->segtype = g_strdup (data->segtype);
-    new_data->origin = g_strdup (data->origin);
-    new_data->pool_lv = g_strdup (data->pool_lv);
-    new_data->data_lv = g_strdup (data->data_lv);
-    new_data->metadata_lv = g_strdup (data->metadata_lv);
-    new_data->roles = g_strdup (data->roles);
-    new_data->move_pv = g_strdup (data->move_pv);
-    new_data->data_percent = data->data_percent;
-    new_data->metadata_percent = data->metadata_percent;
-    new_data->copy_percent = data->copy_percent;
-    new_data->lv_tags = g_strdupv (data->lv_tags);
-    return new_data;
-}
-
-void bd_lvm_lvdata_free (BDLVMLVdata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data->lv_name);
-    g_free (data->vg_name);
-    g_free (data->uuid);
-    g_free (data->attr);
-    g_free (data->segtype);
-    g_free (data->origin);
-    g_free (data->pool_lv);
-    g_free (data->data_lv);
-    g_free (data->metadata_lv);
-    g_free (data->roles);
-    g_free (data->move_pv);
-    g_strfreev (data->lv_tags);
-    g_free (data);
-}
-
-BDLVMCacheStats* bd_lvm_cache_stats_copy (BDLVMCacheStats *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMCacheStats *new = g_new0 (BDLVMCacheStats, 1);
-
-    new->block_size = data->block_size;
-    new->cache_size = data->cache_size;
-    new->cache_used = data->cache_used;
-    new->md_block_size = data->md_block_size;
-    new->md_size = data->md_size;
-    new->md_used = data->md_used;
-    new->read_hits = data->read_hits;
-    new->read_misses = data->read_misses;
-    new->write_hits = data->write_hits;
-    new->write_misses = data->write_misses;
-    new->mode = data->mode;
-
-    return new;
-}
-
-void bd_lvm_cache_stats_free (BDLVMCacheStats *data) {
-    g_free (data);
-}
-
-static gboolean setup_dbus_connection (GError **error) {
-    gchar *addr = NULL;
-
-    addr = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, NULL, error);
-    if (!addr) {
-        g_prefix_error (error, "Failed to get system bus address: ");
-        return FALSE;
-    }
-
-    bus = g_dbus_connection_new_for_address_sync (addr,
-                                                  G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT|
-                                                  G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
-                                                  NULL, NULL, error);
-
-    g_free (addr);
-
-    if (!bus) {
-        g_prefix_error (error, "Failed to create a new connection for the system bus: ");
-        return FALSE;
-    }
-
-    if (g_dbus_connection_is_closed (bus)) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Connection is closed");
-        return FALSE;
-    }
-
-    g_dbus_connection_set_exit_on_close (bus, FALSE);
-
-    return TRUE;
-}
-
-static volatile guint avail_deps = 0;
-static volatile guint avail_dbus_deps = 0;
-static volatile guint avail_features = 0;
-static volatile guint avail_module_deps = 0;
-static GMutex deps_check_lock;
-
-#define DEPS_LVM 0
-#define DEPS_LVM_MASK (1 << DEPS_LVM)
-#define DEPS_LVMDEVICES 1
-#define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES)
-#define DEPS_LVMCONFIG 2
-#define DEPS_LVMCONFIG_MASK (1 << DEPS_LVMCONFIG)
-#define DEPS_LAST 3
-
-static const UtilDep deps[DEPS_LAST] = {
-    {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"},
-    {"lvmdevices", NULL, NULL, NULL},
-    {"lvmconfig", "2.03.17", "--version", "LVM version:\\s+([\\d\\.]+)"},
-};
-
-#define DBUS_DEPS_LVMDBUSD 0
-#define DBUS_DEPS_LVMDBUSD_MASK (1 << DBUS_DEPS_LVMDBUSD)
-#define DBUS_DEPS_LVMDBUSD_WRITECACHE 1
-#define DBUS_DEPS_LVMDBUSD_WRITECACHE_MASK (1 << DBUS_DEPS_LVMDBUSD_WRITECACHE)
-#define DBUS_DEPS_LAST 2
-
-static const DBusDep dbus_deps[DBUS_DEPS_LAST] = {
-    {LVM_BUS_NAME, LVM_OBJ_PREFIX, G_BUS_TYPE_SYSTEM, NULL, NULL, NULL, NULL},
-    {LVM_BUS_NAME, LVM_OBJ_PREFIX, G_BUS_TYPE_SYSTEM, "1.1.0", "Version", MANAGER_INTF, MANAGER_OBJ},
-};
-
-#define FEATURES_VDO 0
-#define FEATURES_VDO_MASK (1 << FEATURES_VDO)
-#define FEATURES_WRITECACHE 0
-#define FEATURES_WRITECACHE_MASK (1 << FEATURES_WRITECACHE)
-#define FEATURES_LAST 2
-
-static const UtilFeatureDep features[FEATURES_LAST] = {
-    {"lvm", "vdo", "segtypes", NULL},
-    {"lvm", "writecache", "segtypes", NULL},
-};
-
-#define MODULE_DEPS_VDO 0
-#define MODULE_DEPS_VDO_MASK (1 << MODULE_DEPS_VDO)
-#define MODULE_DEPS_LAST 1
-
-static const gchar*const module_deps[MODULE_DEPS_LAST] = { "dm-vdo" };
-
-/**
- * bd_lvm_init:
- *
- * Initializes the plugin. **This function is called automatically by the
- * library's initialization functions.**
- *
- */
-gboolean bd_lvm_init (void) {
-    GError *error = NULL;
-
-    /* the check() call should create the DBus connection for us, but let's not
-       completely rely on it */
-    if (G_UNLIKELY (!bus) && !setup_dbus_connection (&error)) {
-        bd_utils_log_format (BD_UTILS_LOG_CRIT, "Failed to setup DBus connection: %s", error->message);
-        g_clear_error (&error);
-        return FALSE;
-    }
-
-    dm_log_with_errno_init ((dm_log_with_errno_fn) redirect_dm_log);
-#ifdef DEBUG
-    dm_log_init_verbose (LOG_DEBUG);
-#else
-    dm_log_init_verbose (LOG_INFO);
-#endif
-
-    return TRUE;
-}
-
-/**
- * bd_lvm_close:
- *
- * Cleans up after the plugin. **This function is called automatically by the
- * library's functions that unload it.**
- *
- */
-void bd_lvm_close (void) {
-    GError *error = NULL;
-
-    /* the check() call should create the DBus connection for us, but let's not
-       completely rely on it */
-    if (!g_dbus_connection_flush_sync (bus, NULL, &error)) {
-        bd_utils_log_format (BD_UTILS_LOG_CRIT, "Failed to flush DBus connection: %s", error->message);
-        g_clear_error (&error);
-    }
-    if (!g_dbus_connection_close_sync (bus, NULL, &error)) {
-        bd_utils_log_format (BD_UTILS_LOG_CRIT, "Failed to close DBus connection: %s", error->message);
-        g_clear_error (&error);
-    }
-
-    dm_log_with_errno_init (NULL);
-    dm_log_init_verbose (0);
-}
-
-/**
- * bd_lvm_is_tech_avail:
- * @tech: the queried tech
- * @mode: a bit mask of queried modes of operation (#BDLVMTechMode) for @tech
- * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
- *
- * Returns: whether the @tech-@mode combination is available -- supported by the
- *          plugin implementation and having all the runtime dependencies available
- */
-gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) {
-    switch (tech) {
-    case BD_LVM_TECH_THIN_CALCS:
-        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
-            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
-                         "Only 'query' supported for thin calculations");
-            return FALSE;
-        } else
-            return TRUE;
-    case BD_LVM_TECH_CALCS:
-        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
-            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
-                         "Only 'query' supported for calculations");
-            return FALSE;
-        } else
-            return TRUE;
-    case BD_LVM_TECH_VDO:
-        return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error) &&
-               check_features (&avail_features, FEATURES_VDO_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
-               check_module_deps (&avail_module_deps, MODULE_DEPS_VDO_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error);
-    case BD_LVM_TECH_WRITECACHE:
-        return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK|DBUS_DEPS_LVMDBUSD_WRITECACHE_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error) &&
-               check_features (&avail_features, FEATURES_WRITECACHE_MASK, features, FEATURES_LAST, &deps_check_lock, error);
-    case BD_LVM_TECH_DEVICES:
-        return check_deps (&avail_deps, DEPS_LVMDEVICES_MASK, deps, DEPS_LAST, &deps_check_lock, error);
-    case BD_LVM_TECH_CONFIG:
-        return check_deps (&avail_deps, DEPS_LVMCONFIG_MASK, deps, DEPS_LAST, &deps_check_lock, error);
-    default:
-        /* everything is supported by this implementation of the plugin */
-        return check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error);
-    }
-}
-
-static gchar** get_existing_objects (const gchar *obj_prefix, GError **error) {
-    GVariant *intro_v = NULL;
-    gchar *intro_data = NULL;
-    GDBusNodeInfo *info = NULL;
-    gchar **ret = NULL;
-    GDBusNodeInfo **nodes;
-    guint64 n_nodes = 0;
-    guint64 i = 0;
-
-    intro_v = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj_prefix, DBUS_INTRO_IFACE,
-                                           "Introspect", NULL, NULL, G_DBUS_CALL_FLAGS_NONE,
-                                           -1, NULL, error);
-    if (!intro_v)
-        /* no introspection data, something went wrong (error must be set) */
-        return NULL;
-
-    g_variant_get (intro_v, "(s)", &intro_data);
-    g_variant_unref (intro_v);
-    info = g_dbus_node_info_new_for_xml (intro_data, error);
-    g_free (intro_data);
-
-    for (nodes = info->nodes; (*nodes); nodes++)
-        n_nodes++;
-
-    ret = g_new0 (gchar*, n_nodes + 1);
-    for (nodes = info->nodes, i=0; (*nodes); nodes++, i++) {
-        ret[i] = g_strdup_printf ("%s/%s", obj_prefix, ((*nodes)->path));
-    }
-    ret[i] = NULL;
-
-    g_dbus_node_info_unref (info);
-
-    return ret;
-}
-
-
-/**
- * get_object_path:
- * @obj_id: get object path for an LVM object (vgname/lvname)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): object path
- */
-static gchar* get_object_path (const gchar *obj_id, GError **error) {
-    GVariant *args = NULL;
-    GVariant *ret = NULL;
-    gchar *obj_path = NULL;
-
-    args = g_variant_new ("(s)", obj_id);
-    /* consumes (frees) the 'args' parameter */
-    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, MANAGER_OBJ, MANAGER_INTF,
-                                       "LookUpByLvmId", args, NULL, G_DBUS_CALL_FLAGS_NONE,
-                                       -1, NULL, error);
-    if (!ret)
-        /* error is already set */
-        return NULL;
-
-    g_variant_get (ret, "(o)", &obj_path);
-    g_variant_unref (ret);
-
-    if (g_strcmp0 (obj_path, "/") == 0) {
-        /* not a valid path (at least for us) */
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
-                     "The object with LVM ID '%s' doesn't exist", obj_id);
-        g_free (obj_path);
-        return NULL;
-    }
-
-    return obj_path;
-}
-
-/**
- * get_object_property:
- * @obj_path: lvmdbusd object path
- * @iface: interface on @obj_path object
- * @property: property to get from @obj_path and @iface
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): object path
- */
-static GVariant* get_object_property (const gchar *obj_path, const gchar *iface, const gchar *property, GError **error) {
-    GVariant *args = NULL;
-    GVariant *ret = NULL;
-    GVariant *real_ret = NULL;
-
-    args = g_variant_new ("(ss)", iface, property);
-
-    /* consumes (frees) the 'args' parameter */
-    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj_path, DBUS_PROPS_IFACE,
-                                       "Get", args, NULL, G_DBUS_CALL_FLAGS_NONE,
-                                       -1, NULL, error);
-    if (!ret) {
-        g_prefix_error (error, "Failed to get %s property of the %s object: ", property, obj_path);
-        return NULL;
-    }
-
-    g_variant_get (ret, "(v)", &real_ret);
-    g_variant_unref (ret);
-
-    return real_ret;
-}
-
-/**
- * get_lvm_object_property:
- * @obj_id: LVM object to get the property for (vgname/lvname)
- * @iface: interface where @property is defined
- * @property: property to get from @obj_id and @iface
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): property variant
- */
-static GVariant* get_lvm_object_property (const gchar *obj_id, const gchar *iface, const gchar *property, GError **error) {
-    gchar *obj_path = NULL;
-    GVariant *ret = NULL;
-
-    obj_path = get_object_path (obj_id, error);
-    if (!obj_path)
-        /* error is already set */
-        return NULL;
-    else {
-        ret = get_object_property (obj_path, iface, property, error);
-        g_free (obj_path);
-        return ret;
-    }
-}
-
-static gboolean unbox_params_and_add (GVariant *params, GVariantBuilder *builder) {
-    GVariantIter iter;
-    GVariant *param = NULL;
-    gboolean ret = FALSE;
-
-    if (g_variant_is_of_type (params, G_VARIANT_TYPE_DICTIONARY)) {
-        g_variant_iter_init (&iter, params);
-        while ((param = g_variant_iter_next_value (&iter))) {
-            g_variant_builder_add_value (builder, param);
-            ret = TRUE;
-        }
-        return ret;
-    }
-
-    if (g_variant_is_of_type (params, G_VARIANT_TYPE_VARIANT)) {
-        param = g_variant_get_variant (params);
-        return unbox_params_and_add (param, builder);
-    }
-
-    if (g_variant_is_container (params)) {
-        g_variant_iter_init (&iter, params);
-        while ((param = g_variant_iter_next_value (&iter)))
-            ret = unbox_params_and_add (param, builder);
-        return ret;
-    }
-
-    return FALSE;
-}
-
-/**
- * call_lvm_method
- * @obj: lvmdbusd object path
- * @intf: interface to call @method on
- * @method: method to call
- * @params: parameters for @method
- * @extra_params: extra parameters for @method
- * @extra_args: extra command line argument to be passed to the LVM command
- * @task_id: (out): task ID to watch progress of the operation
- * @progress_id: (out): progress ID to watch progress of the operation
- * @lock_config: whether to lock %global_config_lock or not (if %FALSE is given, caller is responsible
- *               for holding the lock for this call)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): return value of @method (variant)
- */
-static GVariant* call_lvm_method (const gchar *obj, const gchar *intf, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, guint64 *task_id, guint64 *progress_id, gboolean lock_config, GError **error) {
-    GVariant *config = NULL;
-    GVariant *devices = NULL;
-    GVariant *param = NULL;
-    GVariantIter iter;
-    GVariantBuilder builder;
-    GVariantBuilder extra_builder;
-    GVariant *config_extra_params = NULL;
-    GVariant *tmo = NULL;
-    GVariant *all_params = NULL;
-    GVariant *ret = NULL;
-    gchar *params_str = NULL;
-    gchar *log_msg = NULL;
-    gchar *prog_msg = NULL;
-    const BDExtraArg **extra_p = NULL;
-    gboolean added_extra = FALSE;
-
-    if (!check_dbus_deps (&avail_dbus_deps, DBUS_DEPS_LVMDBUSD_MASK, dbus_deps, DBUS_DEPS_LAST, &deps_check_lock, error))
-        return NULL;
-
-    /* don't allow global config string changes during the run */
-    if (lock_config)
-        g_mutex_lock (&global_config_lock);
-
-    if (global_config_str || global_devices_str || extra_params || extra_args) {
-        if (global_config_str || global_devices_str || extra_args) {
-            /* add the global config to the extra_params */
-            g_variant_builder_init (&extra_builder, G_VARIANT_TYPE_DICTIONARY);
-
-            if (extra_params)
-                added_extra = unbox_params_and_add (extra_params, &extra_builder);
-
-            if (extra_args) {
-                for (extra_p=extra_args; *extra_p; extra_p++) {
-                    g_variant_builder_add (&extra_builder, "{sv}",
-                                           (*extra_p)->opt ? (*extra_p)->opt : "",
-                                           g_variant_new ("s",
-                                                          (*extra_p)->val ? (*extra_p)->val : ""));
-                    added_extra = TRUE;
-                }
-            }
-            if (global_config_str) {
-                config = g_variant_new ("s", global_config_str);
-                g_variant_builder_add (&extra_builder, "{sv}", "--config", config);
-                added_extra = TRUE;
-            }
-            if (global_devices_str) {
-                devices = g_variant_new ("s", global_devices_str);
-                g_variant_builder_add (&extra_builder, "{sv}", "--devices", devices);
-                added_extra = TRUE;
-            }
-
-            if (added_extra)
-                config_extra_params = g_variant_builder_end (&extra_builder);
-            g_variant_builder_clear (&extra_builder);
-        } else
-            /* just use the extra_params */
-            config_extra_params = extra_params;
-    }
-
-    if (!config_extra_params)
-        /* create an empty dictionary with the extra arguments */
-        config_extra_params = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
-
-    /* create new GVariant holding the given parameters with the global
-       config and extra_params merged together appended */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-
-    if (params) {
-        /* add parameters */
-        g_variant_iter_init (&iter, params);
-        while ((param = g_variant_iter_next_value (&iter))) {
-            g_variant_builder_add_value (&builder, param);
-            g_variant_unref (param);
-        }
-    }
-
-    /* add the timeout spec (in seconds) */
-    tmo = g_variant_new ("i", 1);
-    g_variant_builder_add_value (&builder, tmo);
-
-    /* add extra parameters including config */
-    g_variant_builder_add_value (&builder, config_extra_params);
-
-    all_params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    params_str = g_variant_print (all_params, FALSE);
-
-    *task_id = bd_utils_get_next_task_id ();
-    log_msg = g_strdup_printf ("Calling the '%s.%s' method on the '%s' object with the following parameters: '%s'",
-                               intf, method, obj, params_str);
-    bd_utils_log_task_status (*task_id, log_msg);
-    g_free (log_msg);
-
-    /* now do the call with all the parameters */
-    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj, intf, method, all_params,
-                                       NULL, G_DBUS_CALL_FLAGS_NONE, METHOD_CALL_TIMEOUT, NULL, error);
-
-    if (lock_config)
-         g_mutex_unlock (&global_config_lock);
-    prog_msg = g_strdup_printf ("Started the '%s.%s' method on the '%s' object with the following parameters: '%s'",
-                               intf, method, obj, params_str);
-    g_free (params_str);
-    *progress_id = bd_utils_report_started (prog_msg);
-    g_free (prog_msg);
-
-    if (!ret) {
-        g_prefix_error (error, "Failed to call the '%s' method on the '%s' object: ", method, obj);
-        return NULL;
-    }
-
-    return ret;
-}
-
-/**
- * call_lvm_method_sync
- * @obj: lvmdbusd object path
- * @intf: interface to call @method on
- * @method: method to call
- * @params: parameters for @method
- * @extra_params: extra parameters for @method
- * @extra_args: extra command line argument to be passed to the LVM command
- * @lock_config: whether to lock %global_config_lock or not (if %FALSE is given, caller is responsible
- *               for holding the lock for this call)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether calling the method was successful or not
- */
-static gboolean call_lvm_method_sync (const gchar *obj, const gchar *intf, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
-    GVariant *ret = NULL;
-    gchar *obj_path = NULL;
-    gchar *task_path = NULL;
-    guint64 log_task_id = 0;
-    guint64 prog_id = 0;
-    gdouble progress = 0.0;
-    gchar *log_msg = NULL;
-    gboolean completed = FALSE;
-    gint64 error_code = 0;
-    gchar *error_msg = NULL;
-    GError *l_error = NULL;
-
-    ret = call_lvm_method (obj, intf, method, params, extra_params, extra_args, &log_task_id, &prog_id, lock_config, &l_error);
-    bd_utils_log_task_status (log_task_id, "Done.");
-    if (!ret) {
-        if (l_error) {
-            log_msg = g_strdup_printf ("Got error: %s", l_error->message);
-            bd_utils_log_task_status (log_task_id, log_msg);
-            bd_utils_report_finished (prog_id, log_msg);
-            g_free (log_msg);
-            g_propagate_error (error, l_error);
-        } else {
-            bd_utils_log_task_status (log_task_id, "Got unknown error");
-            bd_utils_report_finished (prog_id, "Got unknown error");
-        }
-        return FALSE;
-    }
-    if (g_variant_check_format_string (ret, "((oo))", TRUE)) {
-        g_variant_get (ret, "((oo))", &obj_path, &task_path);
-        if (g_strcmp0 (obj_path, "/") != 0) {
-            log_msg = g_strdup_printf ("Got result: %s", obj_path);
-            bd_utils_log_task_status (log_task_id, log_msg);
-            g_free (log_msg);
-            /* got a valid result, just return */
-            g_variant_unref (ret);
-            g_free (task_path);
-            g_free (obj_path);
-            bd_utils_report_finished (prog_id, "Completed");
-            return TRUE;
-        } else {
-            g_variant_unref (ret);
-            g_free (obj_path);
-            if (g_strcmp0 (task_path, "/") == 0) {
-                log_msg = g_strdup_printf ("Task finished without result and without job started");
-                g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                             "Running '%s' method on the '%s' object failed: %s",
-                             method, obj, log_msg);
-                bd_utils_log_task_status (log_task_id, log_msg);
-                bd_utils_report_finished (prog_id, log_msg);
-                g_free (task_path);
-                g_free (log_msg);
-                return FALSE;
-            }
-        }
-    } else if (g_variant_check_format_string (ret, "(o)", TRUE)) {
-        g_variant_get (ret, "(o)", &task_path);
-        if (g_strcmp0 (task_path, "/") != 0) {
-            g_variant_unref (ret);
-        } else {
-            bd_utils_log_task_status (log_task_id, "No result, no job started");
-            g_free (task_path);
-            bd_utils_report_finished (prog_id, "Completed");
-            g_variant_unref (ret);
-            return TRUE;
-        }
-    } else {
-        g_variant_unref (ret);
-        bd_utils_log_task_status (log_task_id, "Failed to parse the returned value!");
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                     "Failed to parse the returned value!");
-        bd_utils_report_finished (prog_id, "Failed to parse the returned value!");
-        return FALSE;
-    }
-
-    log_msg = g_strdup_printf ("Waiting for job '%s' to finish", task_path);
-    bd_utils_log_task_status (log_task_id, log_msg);
-    g_free (log_msg);
-
-    ret = NULL;
-    while (!completed && !l_error) {
-        g_usleep (PROGRESS_WAIT);
-        ret = get_object_property (task_path, JOB_INTF, "Complete", &l_error);
-        if (ret) {
-            g_variant_get (ret, "b", &completed);
-            g_variant_unref (ret);
-            ret = NULL;
-        }
-        if (!completed && !l_error) {
-            /* let's report progress and wait longer */
-            ret = get_object_property (task_path, JOB_INTF, "Percent", &l_error);
-            if (ret) {
-                g_variant_get (ret, "d", &progress);
-                bd_utils_report_progress (prog_id, (gint) progress, NULL);
-                g_variant_unref (ret);
-                ret = NULL;
-            } else {
-                bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Got error when getting progress: %s", l_error->message);
-                g_clear_error (&l_error);
-            }
-            log_msg = g_strdup_printf ("Still waiting for job '%s' to finish", task_path);
-            bd_utils_log_task_status (log_task_id, log_msg);
-            g_free (log_msg);
-        }
-
-    }
-    log_msg = g_strdup_printf ("Job '%s' finished", task_path);
-    bd_utils_log_task_status (log_task_id, log_msg);
-    g_free (log_msg);
-
-    obj_path = NULL;
-    if (!l_error) {
-        ret = get_object_property (task_path, JOB_INTF, "Result", &l_error);
-        if (!ret) {
-            g_prefix_error (&l_error, "Getting result after waiting for '%s' method of the '%s' object failed: ",
-                            method, obj);
-            bd_utils_report_finished (prog_id, l_error->message);
-            g_propagate_error (error, l_error);
-            g_free (task_path);
-            return FALSE;
-        } else {
-            g_variant_get (ret, "o", &obj_path);
-            g_variant_unref (ret);
-            if (g_strcmp0 (obj_path, "/") != 0) {
-                log_msg = g_strdup_printf ("Got result: %s", obj_path);
-                bd_utils_log_task_status (log_task_id, log_msg);
-                g_free (log_msg);
-            } else {
-                ret = get_object_property (task_path, JOB_INTF, "GetError", &l_error);
-                g_variant_get (ret, "(is)", &error_code, &error_msg);
-                if (error_code != 0) {
-                    if (error_msg) {
-                        log_msg = g_strdup_printf ("Got error: %s", error_msg);
-                        bd_utils_log_task_status (log_task_id, log_msg);
-                        bd_utils_report_finished (prog_id, log_msg);
-                        g_set_error (&l_error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                                     "Running '%s' method on the '%s' object failed: %s",
-                                     method, obj, error_msg);
-                        g_free (log_msg);
-                        g_free (error_msg);
-                    } else {
-                        bd_utils_log_task_status (log_task_id, "Got unknown error");
-                        bd_utils_report_finished (prog_id, "Got unknown error");
-                        g_set_error (&l_error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                                     "Got unknown error when running '%s' method on the '%s' object.",
-                                     method, obj);
-                    }
-                    g_free (task_path);
-                    g_propagate_error (error, l_error);
-                    return FALSE;
-                } else
-                    bd_utils_log_task_status (log_task_id, "No result");
-            }
-            bd_utils_report_finished (prog_id, "Completed");
-            g_free (obj_path);
-
-            /* remove the job object and clean after ourselves */
-            ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, task_path, JOB_INTF, "Remove", NULL,
-                                               NULL, G_DBUS_CALL_FLAGS_NONE, METHOD_CALL_TIMEOUT, NULL, NULL);
-            if (ret)
-                g_variant_unref (ret);
-
-            g_free (task_path);
-            return TRUE;
-        }
-    } else {
-        /* some real error */
-        g_prefix_error (&l_error, "Waiting for '%s' method of the '%s' object to finish failed: ",
-                        method, obj);
-        g_propagate_error (error, l_error);
-        bd_utils_report_finished (prog_id, "Completed");
-        return FALSE;
-    }
-    g_free (task_path);
-
-    return TRUE;
-}
-
-static gboolean call_lvm_obj_method_sync (const gchar *obj_id, const gchar *intf, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
-    g_autofree gchar *obj_path = get_object_path (obj_id, error);
-    if (!obj_path)
-        return FALSE;
-
-    return call_lvm_method_sync (obj_path, intf, method, params, extra_params, extra_args, lock_config, error);
-}
-
-static gboolean call_lv_method_sync (const gchar *vg_name, const gchar *lv_name, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
-    g_autofree gchar *obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    return call_lvm_obj_method_sync (obj_id, LV_INTF, method, params, extra_params, extra_args, lock_config, error);
-}
-
-static gboolean call_thpool_method_sync (const gchar *vg_name, const gchar *pool_name, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
-    g_autofree gchar *obj_id = g_strdup_printf ("%s/%s", vg_name, pool_name);
-
-    return call_lvm_obj_method_sync (obj_id, THPOOL_INTF, method, params, extra_params, extra_args, lock_config, error);
-}
-
-static gboolean call_vdopool_method_sync (const gchar *vg_name, const gchar *pool_name, const gchar *method, GVariant *params, GVariant *extra_params, const BDExtraArg **extra_args, gboolean lock_config, GError **error) {
-    g_autofree gchar *obj_id = g_strdup_printf ("%s/%s", vg_name, pool_name);
-
-    return call_lvm_obj_method_sync (obj_id, VDO_POOL_INTF, method, params, extra_params, extra_args, lock_config, error);
-}
-
-static GVariant* get_lv_property (const gchar *vg_name, const gchar *lv_name, const gchar *property, GError **error) {
-    gchar *lv_spec = NULL;
-    GVariant *ret = NULL;
-
-    lv_spec = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    ret = get_lvm_object_property (lv_spec, LV_CMN_INTF, property, error);
-    g_free (lv_spec);
-
-    return ret;
-}
-
-static GVariant* get_object_properties (const gchar *obj_path, const gchar *iface, GError **error) {
-    GVariant *args = NULL;
-    GVariant *ret = NULL;
-    GVariant *real_ret = NULL;
-
-    args = g_variant_new ("(s)", iface);
-
-    /* consumes (frees) the 'args' parameter */
-    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, obj_path, DBUS_PROPS_IFACE,
-                                       "GetAll", args, NULL, G_DBUS_CALL_FLAGS_NONE,
-                                       -1, NULL, error);
-    if (!ret) {
-        g_prefix_error (error, "Failed to get properties of the %s object: ", obj_path);
-        return NULL;
-    }
-
-    real_ret = g_variant_get_child_value (ret, 0);
-    g_variant_unref (ret);
-
-    return real_ret;
-}
-
-static GVariant* get_lvm_object_properties (const gchar *obj_id, const gchar *iface, GError **error) {
-    GVariant *args = NULL;
-    GVariant *ret = NULL;
-    gchar *obj_path = NULL;
-
-    args = g_variant_new ("(s)", obj_id);
-    /* consumes (frees) the 'args' parameter */
-    ret = g_dbus_connection_call_sync (bus, LVM_BUS_NAME, MANAGER_OBJ, MANAGER_INTF,
-                                       "LookUpByLvmId", args, NULL, G_DBUS_CALL_FLAGS_NONE,
-                                       -1, NULL, error);
-    g_variant_get (ret, "(o)", &obj_path);
-    g_variant_unref (ret);
-
-    if (g_strcmp0 (obj_path, "/") == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
-                     "The object with LVM ID '%s' doesn't exist", obj_id);
-        g_free (obj_path);
-        return NULL;
-    }
-
-    ret = get_object_properties (obj_path, iface, error);
-    g_free (obj_path);
-    return ret;
-}
-
-
-static GVariant* get_pv_properties (const gchar *pv_name, GError **error) {
-    gchar *obj_id = NULL;
-    GVariant *ret = NULL;
-
-    if (!g_str_has_prefix (pv_name, "/dev/")) {
-        obj_id = g_strdup_printf ("/dev/%s", pv_name);
-        ret = get_lvm_object_properties (obj_id, PV_INTF, error);
-        g_free (obj_id);
-    } else
-        ret = get_lvm_object_properties (pv_name, PV_INTF, error);
-
-    return ret;
-}
-
-static GVariant* get_vg_properties (const gchar *vg_name, GError **error) {
-    GVariant *ret = NULL;
-
-    ret = get_lvm_object_properties (vg_name, VG_INTF, error);
-
-    return ret;
-}
-
-static GVariant* get_lv_properties (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    gchar *lvm_spec = NULL;
-    GVariant *ret = NULL;
-
-    lvm_spec = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    ret = get_lvm_object_properties (lvm_spec, LV_CMN_INTF, error);
-    g_free (lvm_spec);
-
-    return ret;
-}
-
-static GVariant* get_vdo_properties (const gchar *vg_name, const gchar *pool_name, GError **error) {
-    gchar *lvm_spec = NULL;
-    GVariant *ret = NULL;
-
-    lvm_spec = g_strdup_printf ("%s/%s", vg_name, pool_name);
-
-    ret = get_lvm_object_properties (lvm_spec, VDO_POOL_INTF, error);
-    g_free (lvm_spec);
-
-    return ret;
-}
-
-static BDLVMPVdata* get_pv_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
-    BDLVMPVdata *data = g_new0 (BDLVMPVdata, 1);
-    GVariantDict dict;
-    gchar *path = NULL;
-    GVariant *vg_props = NULL;
-    GVariant *value = NULL;
-    gsize n_children = 0;
-    gsize i = 0;
-    gchar **tags = NULL;
-    GError *l_error = NULL;
-
-    g_variant_dict_init (&dict, props);
-
-    g_variant_dict_lookup (&dict, "Name", "s", &(data->pv_name));
-    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->pv_uuid));
-    g_variant_dict_lookup (&dict, "FreeBytes", "t", &(data->pv_free));
-    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->pv_size));
-    g_variant_dict_lookup (&dict, "PeStart", "t", &(data->pe_start));
-    g_variant_dict_lookup (&dict, "Missing", "b", &(data->missing));
-
-    value = g_variant_dict_lookup_value (&dict, "Tags", (GVariantType*) "as");
-    if (value) {
-        n_children = g_variant_n_children (value);
-        tags = g_new0 (gchar*, n_children + 1);
-        for (i=0; i < n_children; i++) {
-            g_variant_get_child (value, i, "s", tags+i);
-        }
-        data->pv_tags = tags;
-        g_variant_unref (value);
-    }
-
-    /* returns an object path for the VG */
-    g_variant_dict_lookup (&dict, "Vg", "&o", &path);
-    if (g_strcmp0 (path, "/") == 0) {
-        /* no VG, the PV is not part of any VG */
-        g_variant_dict_clear (&dict);
-        return data;
-    }
-
-    vg_props = get_object_properties (path, VG_INTF, &l_error);
-    g_variant_dict_clear (&dict);
-    if (!vg_props) {
-        if (l_error) {
-            bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Failed to get VG properties for PV %s: %s",
-                                 data->pv_name, l_error->message);
-            g_clear_error (&l_error);
-        }
-        return data;
-    }
-
-    g_variant_dict_init (&dict, vg_props);
-    g_variant_dict_lookup (&dict, "Name", "s", &(data->vg_name));
-    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->vg_uuid));
-    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->vg_size));
-    g_variant_dict_lookup (&dict, "FreeBytes", "t", &(data->vg_free));
-    g_variant_dict_lookup (&dict, "ExtentSizeBytes", "t", &(data->vg_extent_size));
-    g_variant_dict_lookup (&dict, "ExtentCount", "t", &(data->vg_extent_count));
-    g_variant_dict_lookup (&dict, "FreeCount", "t", &(data->vg_free_count));
-    g_variant_dict_lookup (&dict, "PvCount", "t", &(data->vg_pv_count));
-
-    g_variant_dict_clear (&dict);
-    g_variant_unref (vg_props);
-
-    return data;
-}
-
-static BDLVMVGdata* get_vg_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
-    BDLVMVGdata *data = g_new0 (BDLVMVGdata, 1);
-    GVariantDict dict;
-    GVariant *value = NULL;
-    gsize n_children = 0;
-    gsize i = 0;
-    gchar **tags = NULL;
-
-    g_variant_dict_init (&dict, props);
-
-    g_variant_dict_lookup (&dict, "Name", "s", &(data->name));
-    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->uuid));
-
-    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->size));
-    g_variant_dict_lookup (&dict, "FreeBytes", "t", &(data->free));
-    g_variant_dict_lookup (&dict, "ExtentSizeBytes", "t", &(data->extent_size));
-    g_variant_dict_lookup (&dict, "ExtentCount", "t", &(data->extent_count));
-    g_variant_dict_lookup (&dict, "FreeCount", "t", &(data->free_count));
-    g_variant_dict_lookup (&dict, "PvCount", "t", &(data->pv_count));
-    g_variant_dict_lookup (&dict, "Exportable", "b", &(data->exported));
-
-    value = g_variant_dict_lookup_value (&dict, "Tags", (GVariantType*) "as");
-    if (value) {
-        n_children = g_variant_n_children (value);
-        tags = g_new0 (gchar*, n_children + 1);
-        for (i=0; i < n_children; i++) {
-            g_variant_get_child (value, i, "s", tags+i);
-        }
-        data->vg_tags = tags;
-        g_variant_unref (value);
-    }
-
-    g_variant_dict_clear (&dict);
-
-    return data;
-}
-
-static gchar* _lvm_data_lv_name (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *prop = NULL;
-    gchar *obj_id = NULL;
-    gchar *obj_path = NULL;
-    gchar *ret = NULL;
-    gchar *segtype = NULL;
-
-    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    obj_path = get_object_path (obj_id, error);
-    g_free (obj_id);
-    if (!obj_path)
-        return NULL;
-
-    prop = get_lv_property (vg_name, lv_name, "SegType", error);
-    if (!prop)
-        return NULL;
-    g_variant_get_child (prop, 0, "s", &segtype);
-    g_variant_unref (prop);
-
-    if (g_strcmp0 (segtype, "thin-pool") == 0)
-        prop = get_object_property (obj_path, THPOOL_INTF, "DataLv", NULL);
-    else if (g_strcmp0 (segtype, "cache-pool") == 0)
-        prop = get_object_property (obj_path, CACHE_POOL_INTF, "DataLv", NULL);
-    else if (g_strcmp0 (segtype, "vdo-pool") == 0)
-        prop = get_object_property (obj_path, VDO_POOL_INTF, "DataLv", NULL);
-
-    g_free (segtype);
-    g_free (obj_path);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "o", &obj_path);
-    g_variant_unref (prop);
-
-    if (g_strcmp0 (obj_path, "/") == 0) {
-        /* no origin LV */
-        g_free (obj_path);
-        return NULL;
-    }
-    prop = get_object_property (obj_path, LV_CMN_INTF, "Name", error);
-    if (!prop) {
-        g_free (obj_path);
-        return NULL;
-    }
-
-    g_variant_get (prop, "s", &ret);
-    g_variant_unref (prop);
-
-    return g_strstrip (g_strdelimit (ret, "[]", ' '));
-}
-
-static gchar* _lvm_metadata_lv_name (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *prop = NULL;
-    gchar *obj_id = NULL;
-    gchar *obj_path = NULL;
-    gchar *ret = NULL;
-
-    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    obj_path = get_object_path (obj_id, error);
-    g_free (obj_id);
-    if (!obj_path)
-        return NULL;
-
-    prop = get_object_property (obj_path, THPOOL_INTF, "MetaDataLv", NULL);
-    if (!prop)
-        prop = get_object_property (obj_path, CACHE_POOL_INTF, "MetaDataLv", NULL);
-    g_free (obj_path);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "o", &obj_path);
-    g_variant_unref (prop);
-
-    if (g_strcmp0 (obj_path, "/") == 0) {
-        /* no origin LV */
-        g_free (obj_path);
-        return NULL;
-    }
-    prop = get_object_property (obj_path, LV_CMN_INTF, "Name", error);
-    if (!prop) {
-        g_free (obj_path);
-        return NULL;
-    }
-
-    g_variant_get (prop, "s", &ret);
-    g_variant_unref (prop);
-
-    return g_strstrip (g_strdelimit (ret, "[]", ' '));
-}
-
-static BDLVMSEGdata** _lvm_segs (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *prop = NULL;
-    BDLVMSEGdata **segs;
-    gsize n_segs;
-    GVariantIter iter, iter2;
-    GVariant *pv_segs, *pv_name_prop;
-    const gchar *pv;
-    gchar *pv_name;
-    guint64 pv_first_pe, pv_last_pe;
-    int i;
-
-    prop = get_lv_property (vg_name, lv_name, "Devices", error);
-    if (!prop)
-        return NULL;
-
-    /* Count number of segments */
-    n_segs = 0;
-    g_variant_iter_init (&iter, prop);
-    while (g_variant_iter_next (&iter, "(&o@a(tts))", NULL, &pv_segs)) {
-      n_segs += g_variant_n_children (pv_segs);
-      g_variant_unref (pv_segs);
-    }
-
-    if (n_segs == 0) {
-      g_variant_unref (prop);
-      return NULL;
-    }
-
-    /* Build segments */
-    segs = g_new0 (BDLVMSEGdata *, n_segs+1);
-    i = 0;
-    g_variant_iter_init (&iter, prop);
-    while (g_variant_iter_next (&iter, "(&o@a(tts))", &pv, &pv_segs)) {
-      pv_name_prop = get_object_property (pv, PV_INTF, "Name", NULL);
-      if (pv_name_prop) {
-        g_variant_get (pv_name_prop, "&s", &pv_name);
-        g_variant_iter_init (&iter2, pv_segs);
-        while (g_variant_iter_next (&iter2, "(tt&s)", &pv_first_pe, &pv_last_pe, NULL)) {
-          BDLVMSEGdata *seg = g_new0(BDLVMSEGdata, 1);
-          seg->pv_start_pe = pv_first_pe;
-          seg->size_pe = pv_last_pe - pv_first_pe + 1;
-          seg->pvdev = g_strdup (pv_name);
-          segs[i++] = seg;
-        }
-        g_variant_unref (pv_name_prop);
-      }
-      g_variant_unref (pv_segs);
-    }
-
-    g_variant_unref (prop);
-    return segs;
-}
-
-static void _lvm_data_and_metadata_lvs (const gchar *vg_name, const gchar *lv_name,
-                                        gchar ***data_lvs_ret, gchar ***metadata_lvs_ret,
-                                        GError **error) {
-  GVariant *prop;
-  gsize n_hidden_lvs;
-  gchar **data_lvs;
-  gchar **metadata_lvs;
-  GVariantIter iter, iter2;
-  int i_data;
-  int i_metadata;
-  const gchar *sublv;
-  GVariant *sublv_roles_prop;
-  GVariant *sublv_name_prop;
-  gchar *sublv_name;
-  const gchar *role;
-
-  prop = get_lv_property (vg_name, lv_name, "HiddenLvs", error);
-  if (!prop) {
-    *data_lvs_ret = NULL;
-    *metadata_lvs_ret = NULL;
-    return;
-  }
-
-  n_hidden_lvs = g_variant_n_children (prop);
-  data_lvs = g_new0(gchar *, n_hidden_lvs + 1);
-  metadata_lvs = g_new0(gchar *, n_hidden_lvs + 1);
-
-  i_data = 0;
-  i_metadata = 0;
-  g_variant_iter_init (&iter, prop);
-  while (g_variant_iter_next (&iter, "&o", &sublv)) {
-    sublv_roles_prop = get_object_property (sublv, LV_INTF, "Roles", NULL);
-    if (sublv_roles_prop) {
-      sublv_name_prop = get_object_property (sublv, LV_INTF, "Name", NULL);
-      if (sublv_name_prop) {
-        g_variant_get (sublv_name_prop, "s", &sublv_name);
-        if (sublv_name) {
-          sublv_name = g_strstrip (g_strdelimit (sublv_name, "[]", ' '));
-          g_variant_iter_init (&iter2, sublv_roles_prop);
-          while (g_variant_iter_next (&iter2, "&s", &role)) {
-            if (g_strcmp0 (role, "image") == 0) {
-              data_lvs[i_data++] = sublv_name;
-              sublv_name = NULL;
-              break;
-            } else if (g_strcmp0 (role, "metadata") == 0) {
-              metadata_lvs[i_metadata++] = sublv_name;
-              sublv_name = NULL;
-              break;
-            }
-          }
-          g_free (sublv_name);
-        }
-        g_variant_unref (sublv_name_prop);
-      }
-      g_variant_unref (sublv_roles_prop);
-    }
-  }
-
-  *data_lvs_ret = data_lvs;
-  *metadata_lvs_ret = metadata_lvs;
-
-  g_variant_unref (prop);
-  return;
-}
-
-static BDLVMLVdata* get_lv_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
-    BDLVMLVdata *data = g_new0 (BDLVMLVdata, 1);
-    GVariantDict dict;
-    GVariant *value = NULL;
-    gchar *path = NULL;
-    GVariant *name = NULL;
-    gsize n_children = 0;
-    gsize i = 0;
-    gchar **roles = NULL;
-    gchar **tags = NULL;
-
-    g_variant_dict_init (&dict, props);
-
-    g_variant_dict_lookup (&dict, "Name", "s", &(data->lv_name));
-    g_variant_dict_lookup (&dict, "Uuid", "s", &(data->uuid));
-    g_variant_dict_lookup (&dict, "Attr", "s", &(data->attr));
-    g_variant_dict_lookup (&dict, "SizeBytes", "t", &(data->size));
-    g_variant_dict_lookup (&dict, "DataPercent", "u", &(data->data_percent));
-    g_variant_dict_lookup (&dict, "MetaDataPercent", "u", &(data->metadata_percent));
-    g_variant_dict_lookup (&dict, "CopyPercent", "u", &(data->copy_percent));
-
-    /* XXX: how to deal with LVs with multiple segment types? We are just taking
-            the first one now. */
-    value = g_variant_dict_lookup_value (&dict, "SegType", (GVariantType*) "as");
-    if (value) {
-        const gchar *st;
-        g_variant_get_child (value, 0, "&s", &st);
-        if (g_strcmp0 (st, "error") == 0)
-          st = "linear";
-        data->segtype = g_strdup (st);
-        g_variant_unref (value);
-    }
-
-    value = g_variant_dict_lookup_value (&dict, "Roles", (GVariantType*) "as");
-    if (value) {
-        n_children = g_variant_n_children (value);
-        roles = g_new0 (gchar*, n_children + 1);
-        for (i=0; i < n_children; i++)
-            g_variant_get_child (value, i, "&s", roles+i);
-        data->roles = g_strjoinv (",", roles);
-        g_free (roles);
-        g_variant_unref (value);
-    }
-
-    /* returns an object path for the VG */
-    g_variant_dict_lookup (&dict, "Vg", "o", &path);
-    name = get_object_property (path, VG_INTF, "Name", NULL);
-    g_free (path);
-    g_variant_get (name, "s", &(data->vg_name));
-    g_variant_unref (name);
-
-    g_variant_dict_lookup (&dict, "OriginLv", "o", &path);
-    if (g_strcmp0 (path, "/") != 0) {
-        name = get_object_property (path, LV_CMN_INTF, "Name", NULL);
-        g_variant_get (name, "s", &(data->origin));
-        g_variant_unref (name);
-    }
-    g_free (path);
-    path = NULL;
-
-    g_variant_dict_lookup (&dict, "PoolLv", "o", &path);
-    if (g_strcmp0 (path, "/") != 0) {
-        name = get_object_property (path, LV_CMN_INTF, "Name", NULL);
-        g_variant_get (name, "s", &(data->pool_lv));
-        g_variant_unref (name);
-    }
-    g_free (path);
-    path = NULL;
-
-    g_variant_dict_lookup (&dict, "MovePv", "o", &path);
-    if (path && g_strcmp0 (path, "/") != 0) {
-        name = get_object_property (path, PV_INTF, "Name", NULL);
-        g_variant_get (name, "s", &(data->move_pv));
-        g_variant_unref (name);
-    }
-    g_free (path);
-    path = NULL;
-
-    value = g_variant_dict_lookup_value (&dict, "Tags", (GVariantType*) "as");
-    if (value) {
-        n_children = g_variant_n_children (value);
-        tags = g_new0 (gchar*, n_children + 1);
-        for (i=0; i < n_children; i++) {
-            g_variant_get_child (value, i, "s", tags+i);
-        }
-        data->lv_tags = tags;
-        g_variant_unref (value);
-    }
-
-    g_variant_dict_clear (&dict);
-    g_variant_unref (props);
-
-    return data;
-}
-
-static BDLVMVDOPooldata* get_vdo_data_from_props (GVariant *props, GError **error G_GNUC_UNUSED) {
-    BDLVMVDOPooldata *data = g_new0 (BDLVMVDOPooldata, 1);
-    GVariantDict dict;
-    gchar *value = NULL;
-
-    g_variant_dict_init (&dict, props);
-
-    g_variant_dict_lookup (&dict, "OperatingMode", "s", &value);
-    if (g_strcmp0 (value, "recovering") == 0)
-        data->operating_mode = BD_LVM_VDO_MODE_RECOVERING;
-    else if (g_strcmp0 (value, "read-only") == 0)
-        data->operating_mode = BD_LVM_VDO_MODE_READ_ONLY;
-    else if (g_strcmp0 (value, "normal") == 0)
-        data->operating_mode = BD_LVM_VDO_MODE_NORMAL;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO operating mode: %s", value);
-        data->operating_mode = BD_LVM_VDO_MODE_UNKNOWN;
-    }
-    g_free (value);
-    value = NULL;
-
-    g_variant_dict_lookup (&dict, "CompressionState", "s", &value);
-    if (g_strcmp0 (value, "online") == 0)
-        data->compression_state = BD_LVM_VDO_COMPRESSION_ONLINE;
-    else if (g_strcmp0 (value, "offline") == 0)
-        data->compression_state = BD_LVM_VDO_COMPRESSION_OFFLINE;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO compression state: %s", value);
-        data->compression_state = BD_LVM_VDO_COMPRESSION_UNKNOWN;
-    }
-    g_free (value);
-    value = NULL;
-
-    g_variant_dict_lookup (&dict, "IndexState", "s", &value);
-    if (g_strcmp0 (value, "error") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_ERROR;
-    else if (g_strcmp0 (value, "closed") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_CLOSED;
-    else if (g_strcmp0 (value, "opening") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_OPENING;
-    else if (g_strcmp0 (value, "closing") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_CLOSING;
-    else if (g_strcmp0 (value, "offline") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_OFFLINE;
-    else if (g_strcmp0 (value, "online") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_ONLINE;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO index state: %s", value);
-        data->index_state = BD_LVM_VDO_INDEX_UNKNOWN;
-    }
-    g_free (value);
-    value = NULL;
-
-    g_variant_dict_lookup (&dict, "WritePolicy", "s", &value);
-    if (g_strcmp0 (value, "auto") == 0)
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_AUTO;
-    else if (g_strcmp0 (value, "sync") == 0)
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_SYNC;
-    else if (g_strcmp0 (value, "async") == 0)
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_ASYNC;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO write policy: %s", value);
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
-    }
-    g_free (value);
-    value = NULL;
-
-    g_variant_dict_lookup (&dict, "UsedSize", "t", &(data->used_size));
-    g_variant_dict_lookup (&dict, "SavingPercent", "d", &(data->saving_percent));
-
-    g_variant_dict_lookup (&dict, "IndexMemorySize", "t", &(data->index_memory_size));
-
-    g_variant_dict_lookup (&dict, "Compression", "s", &value);
-    if (value && g_strcmp0 (value, "enabled") == 0)
-        data->compression = TRUE;
-    else
-        data->compression = FALSE;
-    g_free (value);
-    value = NULL;
-
-    g_variant_dict_lookup (&dict, "Deduplication", "s", &value);
-    if (value && g_strcmp0 (value, "enabled") == 0)
-        data->deduplication = TRUE;
-    else
-        data->deduplication = FALSE;
-    g_free (value);
-    value = NULL;
-
-    g_variant_dict_clear (&dict);
-    g_variant_unref (props);
-
-    return data;
-}
-
-static GVariant* create_size_str_param (guint64 size, const gchar *unit) {
-    gchar *str = NULL;
-
-    str = g_strdup_printf ("%"G_GUINT64_FORMAT"%s", size, unit ? unit : "");
-    return g_variant_new_take_string (str);
-}
-
-/**
- * bd_lvm_is_supported_pe_size:
- * @size: size (in bytes) to test
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given size is supported physical extent size or not
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error G_GNUC_UNUSED) {
-    return (((size % 2) == 0) && (size >= (MIN_PE_SIZE)) && (size <= (MAX_PE_SIZE)));
-}
-
-/**
- * bd_lvm_get_supported_pe_sizes:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full) (array fixed-size=25): list of supported PE sizes
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 *bd_lvm_get_supported_pe_sizes (GError **error G_GNUC_UNUSED) {
-    guint8 i;
-    guint64 val = MIN_PE_SIZE;
-    guint8 num_items = ((guint8) round (log2 ((double) MAX_PE_SIZE))) - ((guint8) round (log2 ((double) MIN_PE_SIZE))) + 2;
-    guint64 *ret = g_new0 (guint64, num_items);
-
-    for (i=0; (val <= MAX_PE_SIZE); i++, val = val * 2)
-        ret[i] = val;
-
-    ret[num_items-1] = 0;
-
-    return ret;
-}
-
-/**
- * bd_lvm_get_max_lv_size:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: maximum LV size in bytes
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_max_lv_size (GError **error G_GNUC_UNUSED) {
-    return MAX_LV_SIZE;
-}
-
-/**
- * bd_lvm_round_size_to_pe:
- * @size: size to be rounded
- * @pe_size: physical extent (PE) size or 0 to use the default
- * @roundup: whether to round up or down (ceil or floor)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: @size rounded to @pe_size according to the @roundup
- *
- * Rounds given @size up/down to a multiple of @pe_size according to the value
- * of the @roundup parameter. If the rounded value is too big to fit in the
- * return type, the result is rounded down (floored) regardless of the @roundup
- * parameter.
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error G_GNUC_UNUSED) {
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-    guint64 delta = size % pe_size;
-    if (delta == 0)
-        return size;
-
-    if (roundup && (((G_MAXUINT64 - (pe_size - delta)) >= size)))
-        return size + (pe_size - delta);
-    else
-        return size - delta;
-}
-
-/**
- * bd_lvm_get_lv_physical_size:
- * @lv_size: LV size
- * @pe_size: PE size
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: space taken on disk(s) by the LV with given @size
- *
- * Gives number of bytes needed for an LV with the size @lv_size on an LVM stack
- * using given @pe_size.
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_lv_physical_size (guint64 lv_size, guint64 pe_size, GError **error) {
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-
-    /* the LV just takes space rounded up to the multiple of extent size */
-    return bd_lvm_round_size_to_pe (lv_size, pe_size, TRUE, error);
-}
-
-/**
- * bd_lvm_get_thpool_padding:
- * @size: size of the thin pool
- * @pe_size: PE size or 0 if the default value should be used
- * @included: if padding is already included in the size
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: size of the padding needed for a thin pool with the given @size
- *         according to the @pe_size and @included
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_thpool_padding (guint64 size, guint64 pe_size, gboolean included, GError **error G_GNUC_UNUSED) {
-    guint64 raw_md_size;
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-
-    if (included)
-        raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_EXISTS);
-    else
-        raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_NEW);
-
-    return MIN (bd_lvm_round_size_to_pe (raw_md_size, pe_size, TRUE, NULL),
-                bd_lvm_round_size_to_pe (MAX_THPOOL_MD_SIZE, pe_size, TRUE, NULL));
-}
-
-/**
- * bd_lvm_get_thpool_meta_size:
- * @size: size of the thin pool
- * @chunk_size: chunk size of the thin pool or 0 to use the default
- * @n_snapshots: ignored
- * @error: (out) (optional): place to store error (if any)
- *
- * Note: This function will be changed in 3.0: the @n_snapshots parameter
- *       is currently not used and will be removed.
- *
- * Returns: recommended size of the metadata space for the specified pool
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) {
-    guint64 md_size = 0;
-
-    /* based on lvcreate metadata size calculation */
-    md_size = UINT64_C (64) * size / (chunk_size ? chunk_size : DEFAULT_CHUNK_SIZE);
-
-    if (md_size > MAX_THPOOL_MD_SIZE)
-        md_size = MAX_THPOOL_MD_SIZE;
-    else if (md_size < MIN_THPOOL_MD_SIZE)
-        md_size = MIN_THPOOL_MD_SIZE;
-
-    return md_size;
-}
-
-/**
- * bd_lvm_is_valid_thpool_md_size:
- * @size: the size to be tested
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given size is a valid thin pool metadata size or not
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error G_GNUC_UNUSED) {
-    return ((MIN_THPOOL_MD_SIZE <= size) && (size <= MAX_THPOOL_MD_SIZE));
-}
-
-/**
- * bd_lvm_is_valid_thpool_chunk_size:
- * @size: the size to be tested
- * @discard: whether discard/TRIM is required to be supported or not
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given size is a valid thin pool chunk size or not
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error G_GNUC_UNUSED) {
-    gdouble size_log2 = 0.0;
-
-    if ((size < MIN_THPOOL_CHUNK_SIZE) || (size > MAX_THPOOL_CHUNK_SIZE))
-        return FALSE;
-
-    /* To support discard, chunk size must be a power of two. Otherwise it must be a
-       multiple of 64 KiB. */
-    if (discard) {
-        size_log2 = log2 ((double) size);
-        return ABS (((int) round (size_log2)) - size_log2) <= INT_FLOAT_EPS;
-    } else
-        return (size % (64 KiB)) == 0;
-}
-
-/**
- * bd_lvm_pvcreate:
- * @device: the device to make PV from
- * @data_alignment: data (first PE) alignment or 0 to use the default
- * @metadata_size: size of the area reserved for metadata or 0 to use the default
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the PV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_pvcreate (const gchar *device, guint64 data_alignment, guint64 metadata_size, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *param = NULL;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-
-    if (data_alignment != 0 || metadata_size != 0) {
-        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-        if (data_alignment != 0) {
-            param = create_size_str_param (data_alignment, "b");
-            g_variant_builder_add (&builder, "{sv}", "dataalignment", param);
-        }
-
-        if (metadata_size != 0) {
-            param = create_size_str_param (metadata_size, "b");
-            g_variant_builder_add (&builder, "{sv}", "metadatasize", param);
-        }
-        extra_params = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-    }
-
-    params = g_variant_new ("(s)", device);
-
-    return call_lvm_method_sync (MANAGER_OBJ, MANAGER_INTF, "PvCreate", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_pvresize:
- * @device: the device to resize
- * @size: the new requested size of the PV or 0 if it should be adjusted to device's size
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the PV's size was successfully changed or not
- *
- * If given @size different from 0, sets the PV's size to the given value (see
- * pvresize(8)). If given @size 0, adjusts the PV's size to the underlying
- * block device's size.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg **extra, GError **error) {
-    GVariant *params = NULL;
-    gchar *obj_path = get_object_path (device, error);
-    if (!obj_path)
-        return FALSE;
-
-    params = g_variant_new ("(t)", size);
-    return call_lvm_method_sync (obj_path, PV_INTF, "ReSize", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_pvremove:
- * @device: the PV device to be removed/destroyed
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV removal
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the PV was successfully removed/destroyed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
- */
-gboolean bd_lvm_pvremove (const gchar *device, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    GError *l_error = NULL;
-    gboolean ret = FALSE;
-
-    if (access (device, F_OK) != 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
-                     "The device '%s' doesn't exist", device);
-        return FALSE;
-    }
-
-    /* one has to be really persuasive to remove a PV (the double --force is not
-       bug, at least not in this code) */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-    g_variant_builder_add (&builder, "{sv}", "-ff", g_variant_new ("s", ""));
-    g_variant_builder_add (&builder, "{sv}", "--yes", g_variant_new ("s", ""));
-
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-    ret = call_lvm_obj_method_sync (device, PV_INTF, "Remove", NULL, params, extra, TRUE, &l_error);
-    if (!ret && l_error && g_error_matches (l_error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST)) {
-        /* if the object doesn't exist, the given device is not a PV and thus
-           this function should be a noop */
-        g_clear_error (&l_error);
-        ret = TRUE;
-    }
-
-    if (l_error)
-        g_propagate_error (error, l_error);
-    return ret;
-}
-
-/**
- * bd_lvm_pvmove:
- * @src: the PV device to move extents off of
- * @dest: (nullable): the PV device to move extents onto or %NULL
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV move
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the extents from the @src PV where successfully moved or not
- *
- * If @dest is %NULL, VG allocation rules are used for the extents from the @src
- * PV (see pvmove(8)).
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_pvmove (const gchar *src, const gchar *dest, const BDExtraArg **extra, GError **error) {
-    GVariant *prop = NULL;
-    gchar *src_path = NULL;
-    gchar *dest_path = NULL;
-    gchar *vg_obj_path = NULL;
-    GVariantBuilder builder;
-    GVariantType *type = NULL;
-    GVariant *dest_var = NULL;
-    GVariant *params = NULL;
-    GError *l_error = NULL;
-    gboolean ret = FALSE;
-
-    src_path = get_object_path (src, &l_error);
-    if (!src_path || (g_strcmp0 (src_path, "/") == 0)) {
-        if (!l_error)
-            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
-                         "The source PV '%s' doesn't exist", src);
-        else
-            g_propagate_error (error, l_error);
-        return FALSE;
-    }
-    if (dest) {
-        dest_path = get_object_path (dest, &l_error);
-        if (!dest_path || (g_strcmp0 (dest_path, "/") == 0)) {
-            if (!l_error)
-                g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
-                             "The destination PV '%s' doesn't exist", dest);
-            else
-                g_propagate_error (error, l_error);
-            return FALSE;
-        }
-    }
-    prop = get_object_property (src_path, PV_INTF, "Vg", error);
-    if (!prop) {
-        g_free (src_path);
-        return FALSE;
-    }
-    g_variant_get (prop, "o", &vg_obj_path);
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("o", src_path));
-    g_variant_builder_add_value (&builder, g_variant_new ("(tt)", (guint64) 0, (guint64) 0));
-    if (dest) {
-        dest_var = g_variant_new ("(ott)", dest_path, (guint64) 0, (guint64) 0);
-        g_variant_builder_add_value (&builder, g_variant_new_array (NULL, &dest_var, 1));
-    } else {
-        type = g_variant_type_new ("a(ott)");
-        g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
-        g_variant_type_free (type);
-    }
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    ret = call_lvm_method_sync (vg_obj_path, VG_INTF, "Move", params, NULL, extra, TRUE, error);
-
-    g_free (src_path);
-    g_free (dest_path);
-    g_free (vg_obj_path);
-    return ret;
-}
-
-/**
- * bd_lvm_pvscan:
- * @device: (nullable): the device to scan for PVs or %NULL
- * @update_cache: whether to update the lvmetad cache or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV scan
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the system or @device was successfully scanned for PVs or not
- *
- * The @device argument is used only if @update_cache is %TRUE. Otherwise the
- * whole system is scanned for PVs.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_pvscan (const gchar *device, gboolean update_cache, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariantType *type = NULL;
-    GVariant *params = NULL;
-    GVariant *device_var = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    /* update the cache and specify the device (if any) */
-    g_variant_builder_add_value (&builder, g_variant_new_boolean (FALSE));
-    g_variant_builder_add_value (&builder, g_variant_new_boolean (update_cache));
-    if (update_cache && device) {
-        device_var = g_variant_new ("s", device);
-        g_variant_builder_add_value (&builder, g_variant_new_array (NULL, &device_var, 1));
-    } else {
-        type = g_variant_type_new ("as");
-        g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
-        g_variant_type_free (type);
-    }
-    /* (major, minor)`s, we never specify them */
-    type = g_variant_type_new ("a(ii)");
-    g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
-    g_variant_type_free (type);
-
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_lvm_method_sync (MANAGER_OBJ, MANAGER_INTF, "PvScan", params, NULL, extra, TRUE, error);
-}
-
-
-static gboolean _manage_lvm_tags (const gchar *objpath, const gchar *pv_path, const gchar *intf, const gchar **tags, const gchar *func, GError **error) {
-    guint num_tags = g_strv_length ((gchar **) tags);
-    GVariant *params = NULL;
-    GVariant **tags_array = NULL;
-    GVariantBuilder builder;
-    GVariant *pv_var = NULL;
-    gboolean ret = FALSE;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-
-    if (pv_path) {
-        /* PV tags are set from the VG interface so we need to add the PV as an argument here */
-        pv_var = g_variant_new ("o", pv_path);
-        g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_OBJECT_PATH, &pv_var, 1));
-    }
-
-    tags_array = g_new0 (GVariant *, num_tags + 1);
-    for (guint i = 0; i < num_tags; i++)
-        tags_array[i] = g_variant_new_string (tags[i]);
-
-    g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_STRING, tags_array, num_tags));
-
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    ret = call_lvm_method_sync (objpath, intf, func, params, NULL, NULL, TRUE, error);
-    g_free (tags_array);
-    return ret;
-}
-
-/**
- * bd_lvm_add_pv_tags:
- * @device: the device to set PV tags for
- * @tags: (array zero-terminated=1): list of tags to add
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully added to @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_add_pv_tags (const gchar *device, const gchar **tags, GError **error) {
-    BDLVMPVdata *pvinfo = NULL;
-    g_autofree gchar *vg_path = NULL;
-    g_autofree gchar *pv_path = NULL;
-
-    pv_path = get_object_path (device, error);
-    if (!pv_path)
-        return FALSE;
-
-    pvinfo = bd_lvm_pvinfo (device, error);
-    if (!pvinfo)
-        return FALSE;
-
-    if (!pvinfo->vg_name) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Tags can't be added to PVs without a VG");
-        bd_lvm_pvdata_free (pvinfo);
-        return FALSE;
-    }
-
-    vg_path = get_object_path (pvinfo->vg_name, error);
-    bd_lvm_pvdata_free (pvinfo);
-    if (!vg_path)
-        return FALSE;
-
-    return _manage_lvm_tags (vg_path, pv_path, VG_INTF, tags, "PvTagsAdd", error);
-}
-
-/**
- * bd_lvm_delete_pv_tags:
- * @device: the device to set PV tags for
- * @tags: (array zero-terminated=1): list of tags to remove
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully removed from @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_delete_pv_tags (const gchar *device, const gchar **tags, GError **error)  {
-    BDLVMPVdata *pvinfo = NULL;
-    g_autofree gchar *vg_path = NULL;
-    g_autofree gchar *pv_path = NULL;
-
-    pv_path = get_object_path (device, error);
-    if (!pv_path)
-        return FALSE;
-
-    pvinfo = bd_lvm_pvinfo (device, error);
-    if (!pvinfo)
-        return FALSE;
-
-    if (!pvinfo->vg_name) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Tags can't be removed from PVs without a VG");
-        bd_lvm_pvdata_free (pvinfo);
-        return FALSE;
-    }
-
-    vg_path = get_object_path (pvinfo->vg_name, error);
-    bd_lvm_pvdata_free (pvinfo);
-    if (!vg_path)
-        return FALSE;
-
-    return _manage_lvm_tags (vg_path, pv_path, VG_INTF, tags, "PvTagsDel", error);
-}
-/**
- * bd_lvm_pvinfo:
- * @device: a PV to get information about or %NULL
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the PV on the given @device or
- * %NULL in case of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMPVdata* bd_lvm_pvinfo (const gchar *device, GError **error) {
-    GVariant *props = NULL;
-    BDLVMPVdata *ret = NULL;
-
-    props = get_pv_properties (device, error);
-    if (!props)
-        /* the error is already populated */
-        return NULL;
-
-    ret = get_pv_data_from_props (props, error);
-    g_variant_unref (props);
-
-    return ret;
-}
-
-/**
- * bd_lvm_pvs:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (array zero-terminated=1): information about PVs found in the system
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMPVdata** bd_lvm_pvs (GError **error) {
-    gchar **objects = NULL;
-    guint64 n_pvs = 0;
-    GVariant *props = NULL;
-    BDLVMPVdata **ret = NULL;
-    guint64 i = 0;
-    GError *l_error = NULL;
-
-    objects = get_existing_objects (PV_OBJ_PREFIX, &l_error);
-    if (!objects) {
-        if (!l_error) {
-            /* no PVs */
-            ret = g_new0 (BDLVMPVdata*, 1);
-            ret[0] = NULL;
-            return ret;
-        } else {
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-    }
-
-    n_pvs = g_strv_length ((gchar **) objects);
-
-    /* now create the return value -- NULL-terminated array of BDLVMPVdata */
-    ret = g_new0 (BDLVMPVdata*, n_pvs + 1);
-    for (i=0; i < n_pvs; i++) {
-        props = get_object_properties (objects[i], PV_INTF, error);
-        if (!props) {
-            g_strfreev (objects);
-            g_free (ret);
-            return NULL;
-        }
-        ret[i] = get_pv_data_from_props (props, error);
-        g_variant_unref (props);
-        if (!(ret[i])) {
-            g_strfreev (objects);
-            g_free (ret);
-            return NULL;
-        }
-    }
-    ret[i] = NULL;
-
-    g_strfreev (objects);
-    return ret;
-}
-
-/**
- * bd_lvm_vgcreate:
- * @name: name of the newly created VG
- * @pv_list: (array zero-terminated=1): list of PVs the newly created VG should use
- * @pe_size: PE size or 0 if the default value should be used
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG @name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_vgcreate (const gchar *name, const gchar **pv_list, guint64 pe_size, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    gchar *path = NULL;
-    const gchar **pv = NULL;
-    GVariant *pvs = NULL;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-
-    /* build the array of PVs (object paths) */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
-    for (pv=pv_list; *pv; pv++) {
-        path = get_object_path (*pv, error);
-        if (!path) {
-            g_variant_builder_clear (&builder);
-            return FALSE;
-        }
-        g_variant_builder_add_value (&builder, g_variant_new ("o", path));
-    }
-    pvs = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    /* build the params tuple */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("s", name));
-    g_variant_builder_add_value (&builder, pvs);
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    /* pe_size needs to go to extra_params params */
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-    g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--physicalextentsize", create_size_str_param (pe_size, "b")));
-    extra_params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_lvm_method_sync (MANAGER_OBJ, MANAGER_INTF, "VgCreate", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgremove:
- * @vg_name: name of the to be removed VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG removal
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully removed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
- */
-gboolean bd_lvm_vgremove (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Remove", NULL, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgrename:
- * @old_vg_name: old name of the VG to rename
- * @new_vg_name: new name for the @old_vg_name VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG rename
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully renamed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgrename (const gchar *old_vg_name, const gchar *new_vg_name, const BDExtraArg **extra, GError **error) {
-    GVariant *params = g_variant_new ("(s)", new_vg_name);
-    return call_lvm_obj_method_sync (old_vg_name, VG_INTF, "Rename", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgactivate:
- * @vg_name: name of the to be activated VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG activation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully activated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    GVariant *params = g_variant_new ("(t)", (guint64) 0);
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Activate", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgdeactivate:
- * @vg_name: name of the to be deactivated VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG deactivation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully deactivated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    GVariant *params = g_variant_new ("(t)", (guint64) 0);
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Deactivate", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgextend:
- * @vg_name: name of the to be extended VG
- * @device: PV device to extend the @vg_name VG with
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG extension
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG @vg_name was successfully extended with the given @device or not.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
-    g_autofree gchar *pv = NULL;
-    GVariant *pv_var = NULL;
-    GVariant *pvs = NULL;
-    GVariant *params = NULL;
-
-    pv = get_object_path (device, error);
-    if (!pv)
-        return FALSE;
-
-    pv_var = g_variant_new ("o", pv);
-    pvs = g_variant_new_array (NULL, &pv_var, 1);
-    params = g_variant_new_tuple (&pvs, 1);
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Extend", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgreduce:
- * @vg_name: name of the to be reduced VG
- * @device: (nullable): PV device the @vg_name VG should be reduced of or %NULL
- *                        if the VG should be reduced of the missing PVs
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG reduction
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG @vg_name was successfully reduced of the given @device or not
- *
- * Note: This function does not move extents off of the PV before removing
- *       it from the VG. You must do that first by calling #bd_lvm_pvmove.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
-    g_autofree gchar *pv = NULL;
-    GVariantBuilder builder;
-    GVariantType *type = NULL;
-    GVariant *pv_var = NULL;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-
-    if (device) {
-        pv = get_object_path (device, error);
-        if (!pv)
-            return FALSE;
-    }
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    if (device) {
-        /* do not remove missing */
-        pv_var = g_variant_new ("o", pv);
-        g_variant_builder_add_value (&builder, g_variant_new_boolean (FALSE));
-        g_variant_builder_add_value (&builder, g_variant_new_array (NULL, &pv_var, 1));
-        params = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-    } else {
-        /* remove missing */
-        g_variant_builder_add_value (&builder, g_variant_new_boolean (TRUE));
-        type = g_variant_type_new ("ao");
-        g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
-        g_variant_type_free (type);
-        params = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-
-        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-        g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--force", g_variant_new ("s", "")));
-        extra_params = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-    }
-
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Reduce", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_add_vg_tags:
- * @vg_name: the VG to set tags on
- * @tags: (array zero-terminated=1): list of tags to add
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully added to @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
-    g_autofree gchar *obj_path = get_object_path (vg_name, error);
-    if (!obj_path)
-        return FALSE;
-
-    return _manage_lvm_tags (obj_path, NULL, VG_INTF, tags, "TagsAdd", error);
-}
-
-/**
- * bd_lvm_delete_vg_tags:
- * @vg_name: the VG to set tags on
- * @tags: (array zero-terminated=1): list of tags to remove
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully removed from @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
-    g_autofree gchar *obj_path = get_object_path (vg_name, error);
-    if (!obj_path)
-        return FALSE;
-
-    return _manage_lvm_tags (obj_path, NULL, VG_INTF, tags, "TagsDel", error);
-}
-
-static gboolean _vglock_start_stop (const gchar *vg_name, gboolean start, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-    if (start)
-        g_variant_builder_add (&builder, "{sv}", "--lockstart", g_variant_new ("s", ""));
-    else
-        g_variant_builder_add (&builder, "{sv}", "--lockstop", g_variant_new ("s", ""));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "Change", NULL, params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vglock_start:
- * @vg_name: a shared VG to start the lockspace in lvmlockd
- * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
- *                                               (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the lock was successfully started for @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    return _vglock_start_stop (vg_name, TRUE, extra, error);
-}
-
-/**
- * bd_lvm_vglock_stop:
- * @vg_name: a shared VG to stop the lockspace in lvmlockd
- * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
- *                                               (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the lock was successfully stopped for @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    return _vglock_start_stop (vg_name, FALSE, extra, error);
-}
-
-/**
- * bd_lvm_vginfo:
- * @vg_name: a VG to get information about
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the @vg_name VG or %NULL in case
- * of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error) {
-    GVariant *props = NULL;
-    BDLVMVGdata *ret = NULL;
-
-    props = get_vg_properties (vg_name, error);
-    if (!props)
-        /* the error is already populated */
-        return NULL;
-
-    ret = get_vg_data_from_props (props, error);
-    g_variant_unref (props);
-
-    return ret;
-}
-
-/**
- * bd_lvm_vgs:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (array zero-terminated=1): information about VGs found in the system
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVGdata** bd_lvm_vgs (GError **error) {
-    gchar **objects = NULL;
-    guint64 n_vgs = 0;
-    GVariant *props = NULL;
-    BDLVMVGdata **ret = NULL;
-    guint64 i = 0;
-    GError *l_error = NULL;
-
-    objects = get_existing_objects (VG_OBJ_PREFIX, &l_error);
-    if (!objects) {
-        if (!l_error) {
-            /* no VGs */
-            ret = g_new0 (BDLVMVGdata*, 1);
-            ret[0] = NULL;
-            return ret;
-        } else {
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-    }
-
-    n_vgs = g_strv_length ((gchar **) objects);
-
-    /* now create the return value -- NULL-terminated array of BDLVMVGdata */
-    ret = g_new0 (BDLVMVGdata*, n_vgs + 1);
-    for (i=0; i < n_vgs; i++) {
-        props = get_object_properties (objects[i], VG_INTF, error);
-        if (!props) {
-            g_strfreev (objects);
-            g_free (ret);
-            return NULL;
-        }
-        ret[i] = get_vg_data_from_props (props, error);
-        g_variant_unref (props);
-        if (!(ret[i])) {
-            g_strfreev (objects);
-            g_free (ret);
-            return NULL;
-        }
-    }
-    ret[i] = NULL;
-
-    g_strfreev (objects);
-    return ret;
-}
-
-/**
- * bd_lvm_lvorigin:
- * @vg_name: name of the VG containing the queried LV
- * @lv_name: name of the queried LV
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): the origin volume for the @vg_name/@lv_name LV or
- * %NULL if failed to determine (@error) is set in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_lvorigin (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *prop = NULL;
-    gchar *obj_path = NULL;
-    gchar *ret = NULL;
-
-    prop = get_lv_property (vg_name, lv_name, "OriginLv", error);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "o", &obj_path);
-    g_variant_unref (prop);
-
-    if (g_strcmp0 (obj_path, "/") == 0) {
-        /* no origin LV */
-        g_free (obj_path);
-        return NULL;
-    }
-    prop = get_object_property (obj_path, LV_CMN_INTF, "Name", error);
-    if (!prop) {
-        g_free (obj_path);
-        return NULL;
-    }
-
-    g_variant_get (prop, "s", &ret);
-    g_variant_unref (prop);
-
-    return ret;
-}
-
-/**
- * bd_lvm_lvcreate:
- * @vg_name: name of the VG to create a new LV in
- * @lv_name: name of the to-be-created LV
- * @size: requested size of the new LV
- * @type: (nullable): type of the new LV ("striped", "raid1",..., see lvcreate (8))
- * @pv_list: (nullable) (array zero-terminated=1): list of PVs the newly created LV should use or %NULL
- * if not specified
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given @vg_name/@lv_name LV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_lvcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, const gchar *type, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    gchar *path = NULL;
-    const gchar **pv = NULL;
-    GVariant *pvs = NULL;
-    GVariantType *var_type = NULL;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-
-    /* build the array of PVs (object paths) */
-    if (pv_list) {
-        g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
-        for (pv=pv_list; *pv; pv++) {
-            path = get_object_path (*pv, error);
-            if (!path) {
-                g_variant_builder_clear (&builder);
-                return FALSE;
-            }
-            g_variant_builder_add_value (&builder, g_variant_new ("(ott)", path, (guint64) 0, (guint64) 0));
-        }
-        pvs = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-    } else {
-        var_type = g_variant_type_new ("a(ott)");
-        pvs = g_variant_new_array (var_type, NULL, 0);
-        g_variant_type_free (var_type);
-    }
-
-    /* build the params tuple */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
-    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
-    g_variant_builder_add_value (&builder, pvs);
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    if (type) {
-        /* and now the extra_params params */
-        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-        if (pv_list && g_strcmp0 (type, "striped") == 0)
-            g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "stripes", g_variant_new ("i", g_strv_length ((gchar **) pv_list))));
-        else
-            g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "type", g_variant_new ("s", type)));
-        extra_params = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-    }
-
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "LvCreate", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_lvremove:
- * @vg_name: name of the VG containing the to-be-removed LV
- * @lv_name: name of the to-be-removed LV
- * @force: whether to force removal or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV removal
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully removed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
- */
-gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean force, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *extra_params = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-    /* '--yes' is needed if DISCARD is enabled */
-    g_variant_builder_add (&builder, "{sv}", "--yes", g_variant_new ("s", ""));
-    if (force) {
-        g_variant_builder_add (&builder, "{sv}", "--force", g_variant_new ("s", ""));
-    }
-    extra_params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_lv_method_sync (vg_name, lv_name, "Remove", NULL, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_lvrename:
- * @vg_name: name of the VG containing the to-be-renamed LV
- * @lv_name: name of the to-be-renamed LV
- * @new_name: new name for the @vg_name/@lv_name LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV rename
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully renamed to
- * @vg_name/@new_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error) {
-    GVariant *params = NULL;
-
-    params = g_variant_new ("(s)", new_name);
-    return call_lv_method_sync (vg_name, lv_name, "Rename", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_lvresize:
- * @vg_name: name of the VG containing the to-be-resized LV
- * @lv_name: name of the to-be-resized LV
- * @size: the requested new size of the LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully resized or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariantType *type = NULL;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-    gboolean success = FALSE;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
-    type = g_variant_type_new ("a(ott)");
-    g_variant_builder_add_value (&builder, g_variant_new_array (type, NULL, 0));
-    g_variant_type_free (type);
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    /* Starting with 2.03.19 we need to add an extra option to avoid
-       any filesystem related checks by lvresize.
-    */
-    success = bd_utils_check_util_version (deps[DEPS_LVM].name, LVM_VERSION_FSRESIZE,
-                                           deps[DEPS_LVM].ver_arg, deps[DEPS_LVM].ver_regexp, NULL);
-    if (success) {
-      g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-      g_variant_builder_add (&builder, "{sv}", "--fs", g_variant_new ("s", "ignore"));
-      extra_params = g_variant_builder_end (&builder);
-      g_variant_builder_clear (&builder);
-    }
-
-    return call_lv_method_sync (vg_name, lv_name, "Resize", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_lvrepair:
- * @vg_name: name of the VG containing the to-be-repaired LV
- * @lv_name: name of the to-be-repaired LV
- * @pv_list: (array zero-terminated=1): list of PVs to be used for the repair
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV repair
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully repaired or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvrepair (const gchar *vg_name, const gchar *lv_name, const gchar **pv_list,
-                          const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    gchar *path = NULL;
-    const gchar **pv = NULL;
-    GVariant *pvs = NULL;
-
-    /* build the array of PVs (object paths) */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
-    for (pv=pv_list; *pv; pv++) {
-        path = get_object_path (*pv, error);
-        if (!path) {
-            g_variant_builder_clear (&builder);
-            return FALSE;
-        }
-        g_variant_builder_add_value (&builder, g_variant_new ("o", path));
-    }
-    pvs = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, pvs);
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_lv_method_sync (vg_name, lv_name, "RepairRaidLv", params, NULL, extra, TRUE, error);
-
-  return FALSE;
-}
-
-/**
- * bd_lvm_lvactivate:
- * @vg_name: name of the VG containing the to-be-activated LV
- * @lv_name: name of the to-be-activated LV
- * @ignore_skip: whether to ignore the skip flag or not
- * @shared: whether to activate the LV in shared mode (used for shared LVM setups with lvmlockd,
- *          use %FALSE if not sure)
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV activation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully activated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) {
-    GVariant *params = NULL;
-    GVariantBuilder builder;
-    GVariant *extra_params = NULL;
-
-    if (shared)
-        params = g_variant_new ("(t)", (guint64) 1 << 6);
-    else
-        params = g_variant_new ("(t)", (guint64) 0);
-
-    if (ignore_skip) {
-        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-        g_variant_builder_add (&builder, "{sv}", "-K", g_variant_new ("s", ""));
-        extra_params = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-    }
-
-    return call_lv_method_sync (vg_name, lv_name, "Activate", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_lvdeactivate:
- * @vg_name: name of the VG containing the to-be-deactivated LV
- * @lv_name: name of the to-be-deactivated LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV deactivation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully deactivated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error) {
-    GVariant *params = g_variant_new ("(t)", (guint64) 0);
-    return call_lv_method_sync (vg_name, lv_name, "Deactivate", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_lvsnapshotcreate:
- * @vg_name: name of the VG containing the LV a new snapshot should be created of
- * @origin_name: name of the LV a new snapshot should be created of
- * @snapshot_name: name of the to-be-created snapshot
- * @size: requested size for the snapshot
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name LV
- * was successfully created or not.
- *
- * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("s", snapshot_name));
-    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_lv_method_sync (vg_name, origin_name, "Snapshot", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_lvsnapshotmerge:
- * @vg_name: name of the VG containing the to-be-merged LV snapshot
- * @snapshot_name: name of the to-be-merged LV snapshot
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot merge
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@snapshot_name LV snapshot was successfully merged or not
- *
- * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error) {
-    gchar *obj_id = NULL;
-    gchar *obj_path = NULL;
-
-    /* get object path for vg_name/snapshot_name and call SNAP_INTF, "Merge" */
-    obj_id = g_strdup_printf ("%s/%s", vg_name, snapshot_name);
-    obj_path = get_object_path (obj_id, error);
-    g_free (obj_id);
-    if (!obj_path)
-        return FALSE;
-
-    return call_lvm_method_sync (obj_path, SNAP_INTF, "Merge", NULL, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_add_lv_tags:
- * @vg_name: name of the VG that contains the LV to set tags on
- * @lv_name: name of the LV to set tags on
- * @tags: (array zero-terminated=1): list of tags to add
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully added to @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_add_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
-    g_autofree gchar *obj_id = NULL;
-    g_autofree gchar *obj_path = NULL;
-
-    /* get object path for vg_name/lv_name */
-    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    obj_path = get_object_path (obj_id, error);
-    if (!obj_path)
-        return FALSE;
-
-    return _manage_lvm_tags (obj_path, NULL, LV_INTF, tags, "TagsAdd", error);
-}
-
-/**
- * bd_lvm_delete_lv_tags:
- * @vg_name: name of the VG that contains the LV to set tags on
- * @lv_name: name of the LV to set tags on
- * @tags: (array zero-terminated=1): list of tags to remove
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully removed from @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_delete_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
-    g_autofree gchar *obj_id = NULL;
-    g_autofree gchar *obj_path = NULL;
-
-    /* get object path for vg_name/lv_name */
-    obj_id = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    obj_path = get_object_path (obj_id, error);
-    if (!obj_path)
-        return FALSE;
-
-    return _manage_lvm_tags (obj_path, NULL, LV_INTF, tags, "TagsDel", error);
-}
-
-/**
- * bd_lvm_lvinfo:
- * @vg_name: name of the VG that contains the LV to get information about
- * @lv_name: name of the LV to get information about
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
- * of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMLVdata* bd_lvm_lvinfo (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *props = NULL;
-    BDLVMLVdata* ret = NULL;
-
-    props = get_lv_properties (vg_name, lv_name, error);
-    if (!props)
-        /* the error is already populated */
-        return NULL;
-
-    ret = get_lv_data_from_props (props, error);
-    if (!ret)
-        return NULL;
-
-    if (g_strcmp0 (ret->segtype, "thin-pool") == 0 ||
-        g_strcmp0 (ret->segtype, "cache-pool") == 0) {
-        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
-        ret->metadata_lv = _lvm_metadata_lv_name (vg_name, lv_name, NULL);
-    }
-    if (g_strcmp0 (ret->segtype, "vdo-pool") == 0) {
-        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
-    }
-
-    return ret;
-}
-
-BDLVMLVdata* bd_lvm_lvinfo_tree (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *props = NULL;
-    BDLVMLVdata* ret = NULL;
-
-    props = get_lv_properties (vg_name, lv_name, error);
-    if (!props)
-        /* the error is already populated */
-        return NULL;
-
-    ret = get_lv_data_from_props (props, error);
-    if (!ret)
-        return NULL;
-
-    if (g_strcmp0 (ret->segtype, "thin-pool") == 0 ||
-        g_strcmp0 (ret->segtype, "cache-pool") == 0) {
-        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
-        ret->metadata_lv = _lvm_metadata_lv_name (vg_name, lv_name, NULL);
-    }
-    if (g_strcmp0 (ret->segtype, "vdo-pool") == 0) {
-        ret->data_lv = _lvm_data_lv_name (vg_name, lv_name, NULL);
-    }
-    ret->segs = _lvm_segs (vg_name, lv_name, NULL);
-    _lvm_data_and_metadata_lvs (vg_name, lv_name, &ret->data_lvs, &ret->metadata_lvs, NULL);
-
-    return ret;
-}
-
-static gchar* get_lv_vg_name (const gchar *lv_obj_path, GError **error) {
-    GVariant *value = NULL;
-    gchar *vg_obj_path = NULL;
-    gchar *ret = NULL;
-
-    value = get_object_property (lv_obj_path, LV_CMN_INTF, "Vg", error);
-    g_variant_get (value, "o", &vg_obj_path);
-    g_variant_unref (value);
-
-    value = get_object_property (vg_obj_path, VG_INTF, "Name", error);
-    g_variant_get (value, "s", &ret);
-    g_free (vg_obj_path);
-    g_variant_unref (value);
-
-    return ret;
-}
-
-/**
- * filter_lvs_by_vg: (skip)
- *
- * Filter LVs by VG name and prepend the matching ones to the @out list.
- */
-static gboolean filter_lvs_by_vg (gchar **lvs, const gchar *vg_name, GSList **out, guint64 *n_lvs, GError **error) {
-    gchar **lv_p = NULL;
-    gchar *lv_vg_name = NULL;
-    gboolean success = TRUE;
-
-    if (!lvs)
-        /* nothing to do */
-        return TRUE;
-
-    for (lv_p=lvs; *lv_p; lv_p++) {
-        if (vg_name) {
-            lv_vg_name = get_lv_vg_name (*lv_p, error);
-            if (!lv_vg_name) {
-                g_free (*lv_p);
-                success = FALSE;
-                continue;
-            }
-
-            if (g_strcmp0 (lv_vg_name, vg_name) == 0) {
-                *out = g_slist_prepend (*out, *lv_p);
-                (*n_lvs)++;
-            } else {
-                g_free (*lv_p);
-            }
-
-            g_free (lv_vg_name);
-        } else {
-            *out = g_slist_prepend (*out, *lv_p);
-            (*n_lvs)++;
-        }
-    }
-    return success;
-}
-
-/**
- * bd_lvm_lvs:
- * @vg_name: (nullable): name of the VG to get information about LVs from
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (array zero-terminated=1): information about LVs found in the given
- * @vg_name VG or in system if @vg_name is %NULL
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMLVdata** bd_lvm_lvs (const gchar *vg_name, GError **error) {
-    gchar **lvs = NULL;
-    guint64 n_lvs = 0;
-    GVariant *props = NULL;
-    BDLVMLVdata **ret = NULL;
-    guint64 j = 0;
-    GSList *matched_lvs = NULL;
-    GSList *lv = NULL;
-    gboolean success = FALSE;
-    GError *l_error = NULL;
-
-    lvs = get_existing_objects (LV_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (THIN_POOL_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (CACHE_POOL_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (VDO_POOL_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (HIDDEN_LV_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    if (n_lvs == 0) {
-        /* no LVs */
-        ret = g_new0 (BDLVMLVdata*, 1);
-        ret[0] = NULL;
-        g_slist_free_full (matched_lvs, g_free);
-        return ret;
-    }
-
-    /* we have been prepending to the list so far, but it will be nicer if we
-       reverse it (to get back the original order) */
-    matched_lvs = g_slist_reverse (matched_lvs);
-
-    /* now create the return value -- NULL-terminated array of BDLVMLVdata */
-    ret = g_new0 (BDLVMLVdata*, n_lvs + 1);
-
-    lv = matched_lvs;
-    while (lv) {
-        props = get_object_properties (lv->data, LV_CMN_INTF, &l_error);
-        if (!props) {
-            g_slist_free_full (matched_lvs, g_free);
-            g_free (ret);
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-        ret[j] = get_lv_data_from_props (props, &l_error);
-        if (!(ret[j])) {
-            g_slist_free_full (matched_lvs, g_free);
-            for (guint64 i = 0; i < j; i++)
-                bd_lvm_lvdata_free (ret[i]);
-            g_free (ret);
-            g_propagate_error (error, l_error);
-            return NULL;
-        } else if ((g_strcmp0 (ret[j]->segtype, "thin-pool") == 0) ||
-                   (g_strcmp0 (ret[j]->segtype, "cache-pool") == 0)) {
-            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
-            ret[j]->metadata_lv = _lvm_metadata_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
-        } else if (g_strcmp0 (ret[j]->segtype, "vdo-pool") == 0) {
-            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
-        }
-        if (l_error) {
-            g_slist_free_full (matched_lvs, g_free);
-            for (guint64 i = 0; i <= j; i++)
-                bd_lvm_lvdata_free (ret[i]);
-            g_free (ret);
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-        j++;
-        lv = g_slist_next (lv);
-    }
-    g_slist_free_full (matched_lvs, g_free);
-
-    ret[j] = NULL;
-    return ret;
-}
-
-BDLVMLVdata** bd_lvm_lvs_tree (const gchar *vg_name, GError **error) {
-    gchar **lvs = NULL;
-    guint64 n_lvs = 0;
-    GVariant *props = NULL;
-    BDLVMLVdata **ret = NULL;
-    guint64 j = 0;
-    GSList *matched_lvs = NULL;
-    GSList *lv = NULL;
-    gboolean success = FALSE;
-    GError *l_error = NULL;
-
-    lvs = get_existing_objects (LV_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (THIN_POOL_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (CACHE_POOL_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (VDO_POOL_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    lvs = get_existing_objects (HIDDEN_LV_OBJ_PREFIX, &l_error);
-    if (!lvs && l_error) {
-        g_propagate_error (error, l_error);
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-    success = filter_lvs_by_vg (lvs, vg_name, &matched_lvs, &n_lvs, error);
-    g_free (lvs);
-    if (!success) {
-        g_slist_free_full (matched_lvs, g_free);
-        return NULL;
-    }
-
-    if (n_lvs == 0) {
-        /* no LVs */
-        ret = g_new0 (BDLVMLVdata*, 1);
-        ret[0] = NULL;
-        g_slist_free_full (matched_lvs, g_free);
-        return ret;
-    }
-
-    /* we have been prepending to the list so far, but it will be nicer if we
-       reverse it (to get back the original order) */
-    matched_lvs = g_slist_reverse (matched_lvs);
-
-    /* now create the return value -- NULL-terminated array of BDLVMLVdata */
-    ret = g_new0 (BDLVMLVdata*, n_lvs + 1);
-
-    lv = matched_lvs;
-    while (lv) {
-        props = get_object_properties (lv->data, LV_CMN_INTF, &l_error);
-        if (!props) {
-            g_slist_free_full (matched_lvs, g_free);
-            g_free (ret);
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-        ret[j] = get_lv_data_from_props (props, &l_error);
-        if (!(ret[j])) {
-            g_slist_free_full (matched_lvs, g_free);
-            for (guint64 i = 0; i < j; i++)
-                bd_lvm_lvdata_free (ret[i]);
-            g_free (ret);
-            g_propagate_error (error, l_error);
-            return NULL;
-        } else if ((g_strcmp0 (ret[j]->segtype, "thin-pool") == 0) ||
-                   (g_strcmp0 (ret[j]->segtype, "cache-pool") == 0)) {
-            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
-            ret[j]->metadata_lv = _lvm_metadata_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
-        } else if (g_strcmp0 (ret[j]->segtype, "vdo-pool") == 0) {
-            ret[j]->data_lv = _lvm_data_lv_name (ret[j]->vg_name, ret[j]->lv_name, &l_error);
-        }
-        ret[j]->segs = _lvm_segs (ret[j]->vg_name, ret[j]->lv_name, &l_error);
-        _lvm_data_and_metadata_lvs (ret[j]->vg_name, ret[j]->lv_name, &ret[j]->data_lvs, &ret[j]->metadata_lvs,
-                                    &l_error);
-        if (l_error) {
-            g_slist_free_full (matched_lvs, g_free);
-            for (guint64 i = 0; i <= j; i++)
-                bd_lvm_lvdata_free (ret[i]);
-            g_free (ret);
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-        j++;
-        lv = g_slist_next (lv);
-    }
-    g_slist_free_full (matched_lvs, g_free);
-
-    ret[j] = NULL;
-    return ret;
-}
-
-/**
- * bd_lvm_thpoolcreate:
- * @vg_name: name of the VG to create a thin pool in
- * @lv_name: name of the to-be-created pool LV
- * @size: requested size of the to-be-created pool
- * @md_size: requested metadata size or 0 to use the default
- * @chunk_size: requested chunk size or 0 to use the default
- * @profile: (nullable): profile to use (see lvm(8) for more information) or %NULL to use
- *                         the default
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name thin pool was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thpoolcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, guint64 md_size, guint64 chunk_size, const gchar *profile, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-    GVariant *param = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
-    g_variant_builder_add_value (&builder, g_variant_new_uint64 (size));
-    g_variant_builder_add_value (&builder, g_variant_new_boolean (TRUE));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-    if (md_size != 0) {
-        param = create_size_str_param (md_size, "b");
-        g_variant_builder_add (&builder, "{sv}", "poolmetadatasize", param);
-    }
-    if (chunk_size != 0) {
-        param = create_size_str_param (chunk_size, "b");
-        g_variant_builder_add (&builder, "{sv}", "chunksize", param);
-    }
-    if (profile) {
-        g_variant_builder_add (&builder, "{sv}", "profile", g_variant_new ("s", profile));
-    }
-    extra_params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_lvm_obj_method_sync (vg_name, VG_INTF, "LvCreateLinear", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_thlvcreate:
- * @vg_name: name of the VG containing the thin pool providing extents for the to-be-created thin LV
- * @pool_name: name of the pool LV providing extents for the to-be-created thin LV
- * @lv_name: name of the to-be-created thin LV
- * @size: requested virtual size of the to-be-created thin LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name thin LV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thlvcreate (const gchar *vg_name, const gchar *pool_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
-    g_variant_builder_add_value (&builder, g_variant_new ("t", size));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    return call_thpool_method_sync (vg_name, pool_name, "LvCreate", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_thlvpoolname:
- * @vg_name: name of the VG containing the queried thin LV
- * @lv_name: name of the queried thin LV
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
- * thin LV or %NULL if failed to determine (@error) is set in those cases)
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_thlvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *prop = NULL;
-    gboolean is_thin = FALSE;
-    gchar *pool_obj_path = NULL;
-    gchar *ret = NULL;
-
-    prop = get_lv_property (vg_name, lv_name, "IsThinVolume", error);
-    if (!prop)
-        return NULL;
-    is_thin = g_variant_get_boolean (prop);
-    g_variant_unref (prop);
-
-    if (!is_thin) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
-                     "The LV '%s' is not a thin LV and thus have no thin pool", lv_name);
-        return NULL;
-    }
-    prop = get_lv_property (vg_name, lv_name, "PoolLv", error);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "o", &pool_obj_path);
-    g_variant_unref (prop);
-
-    prop = get_object_property (pool_obj_path, LV_CMN_INTF, "Name", error);
-    g_free (pool_obj_path);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "s", &ret);
-    g_variant_unref (prop);
-
-    return ret;
-}
-
-/**
- * bd_lvm_thsnapshotcreate:
- * @vg_name: name of the VG containing the thin LV a new snapshot should be created of
- * @origin_name: name of the thin LV a new snapshot should be created of
- * @snapshot_name: name of the to-be-created snapshot
- * @pool_name: (nullable): name of the thin pool to create the snapshot in or %NULL if not specified
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV snapshot creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name
- * thin LV was successfully created or not.
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("s", snapshot_name));
-    g_variant_builder_add_value (&builder, g_variant_new ("t", (guint64) 0));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    if (pool_name) {
-        g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-        g_variant_builder_add (&builder, "{sv}", "thinpool", g_variant_new ("s", pool_name));
-        extra_params = g_variant_builder_end (&builder);
-        g_variant_builder_clear (&builder);
-    }
-
-    return call_lv_method_sync (vg_name, origin_name, "Snapshot", params, extra_params, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_set_global_config:
- * @new_config: (nullable): string representation of the new global libblockdev LVM
- *                          configuration to set or %NULL to reset to default
- * @error: (out) (optional): place to store error (if any)
- *
- *
- * Note: This function sets configuration options for LVM calls internally
- *       in libblockdev, it doesn't change the global lvm.conf config file.
- *       Calling this function with `backup {backup=0 archive=0}` for example
- *       means `--config=backup {backup=0 archive=0}"` will be added to all
- *       calls libblockdev makes.
- *
- * Returns: whether the new requested global config @new_config was successfully
- *          set or not
- *
- * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
- */
-gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error G_GNUC_UNUSED) {
-    /* XXX: the error attribute will likely be used in the future when
-       some validation comes into the game */
-
-    g_mutex_lock (&global_config_lock);
-
-    /* first free the old value */
-    g_free (global_config_str);
-
-    /* now store the new one */
-    if (!new_config || g_strcmp0 (new_config, "") == 0)
-         global_config_str = NULL;
-    else
-        global_config_str = g_strdup (new_config);
-
-    g_mutex_unlock (&global_config_lock);
-    return TRUE;
-}
-
-/**
- * bd_lvm_get_global_config:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): a copy of a string representation of the currently
- *                           set libblockdev LVM global configuration
- *
- * Note: This function does not change the global `lvm.conf` config
- *       file, see %bd_lvm_set_global_config for details.
- *
- * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
- */
-gchar* bd_lvm_get_global_config (GError **error G_GNUC_UNUSED) {
-    gchar *ret = NULL;
-
-    g_mutex_lock (&global_config_lock);
-    ret = g_strdup (global_config_str ? global_config_str : "");
-    g_mutex_unlock (&global_config_lock);
-
-    return ret;
-}
-
-/**
- * bd_lvm_set_devices_filter:
- * @devices: (nullable) (array zero-terminated=1): list of devices for lvm commands to work on
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the devices filter was successfully set or not
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error) {
-    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
-        return FALSE;
-
-    g_mutex_lock (&global_config_lock);
-
-    /* first free the old value */
-    g_free (global_devices_str);
-
-    /* now store the new one */
-    if (!devices || !(*devices))
-        global_devices_str = NULL;
-    else
-        global_devices_str = g_strjoinv (",", (gchar **) devices);
-
-    g_mutex_unlock (&global_config_lock);
-    return TRUE;
-}
-
-/**
- * bd_lvm_get_devices_filter:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
- *                                                     the currently set LVM devices filter
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gchar** bd_lvm_get_devices_filter (GError **error G_GNUC_UNUSED) {
-    gchar **ret = NULL;
-
-    g_mutex_lock (&global_config_lock);
-
-    if (global_devices_str)
-        ret = g_strsplit (global_devices_str, ",", -1);
-    else
-        ret = NULL;
-
-    g_mutex_unlock (&global_config_lock);
-
-    return ret;
-}
-
-/**
- * bd_lvm_cache_get_default_md_size:
- * @cache_size: size of the cache to determine MD size for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: recommended default size of the cache metadata LV or 0 in case of error
- *
- * Tech category: %BD_LVM_TECH_CACHE_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error G_GNUC_UNUSED) {
-    return MAX ((guint64) cache_size / 1000, MIN_CACHE_MD_SIZE);
-}
-
-/**
- * get_lv_type_from_flags: (skip)
- * @meta: getting type for a (future) metadata LV
- *
- * Get LV type string from flags.
- */
-static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error G_GNUC_UNUSED) {
-    if (!meta) {
-        if (flags & BD_LVM_CACHE_POOL_STRIPED)
-            return "striped";
-        else if (flags & BD_LVM_CACHE_POOL_RAID1)
-            return "raid1";
-        else if (flags & BD_LVM_CACHE_POOL_RAID5)
-            return "raid5";
-        else if (flags & BD_LVM_CACHE_POOL_RAID6)
-            return "raid6";
-        else if (flags & BD_LVM_CACHE_POOL_RAID10)
-            return "raid10";
-        else
-            return NULL;
-    } else {
-        if (flags & BD_LVM_CACHE_POOL_META_STRIPED)
-            return "striped";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID1)
-            return "raid1";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID5)
-            return "raid5";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID6)
-            return "raid6";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID10)
-            return "raid10";
-        else
-            return NULL;
-    }
-}
-
-/**
- * bd_lvm_cache_get_mode_str:
- * @mode: mode to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @mode or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error) {
-    if (mode == BD_LVM_CACHE_MODE_WRITETHROUGH)
-        return "writethrough";
-    else if (mode == BD_LVM_CACHE_MODE_WRITEBACK)
-        return "writeback";
-    else if (mode == BD_LVM_CACHE_MODE_UNKNOWN)
-        return "unknown";
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Invalid mode given: %d", mode);
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_cache_get_mode_from_str:
- * @mode_str: string representation of a cache mode
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: cache mode for the @mode_str or %BD_LVM_CACHE_MODE_UNKNOWN if
- *          failed to determine
- *
- * Tech category: always provided/supported
- */
-BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error) {
-    if (g_strcmp0 (mode_str, "writethrough") == 0)
-        return BD_LVM_CACHE_MODE_WRITETHROUGH;
-    else if (g_strcmp0 (mode_str, "writeback") == 0)
-        return BD_LVM_CACHE_MODE_WRITEBACK;
-    else if (g_strcmp0 (mode_str, "unknown") == 0)
-        return BD_LVM_CACHE_MODE_UNKNOWN;
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Invalid mode given: %s", mode_str);
-        return BD_LVM_CACHE_MODE_UNKNOWN;
-    }
-}
-
-/**
- * bd_lvm_cache_create_pool:
- * @vg_name: name of the VG to create @pool_name in
- * @pool_name: name of the cache pool LV to create
- * @pool_size: desired size of the cache pool @pool_name
- * @md_size: desired size of the @pool_name cache pool's metadata LV or 0 to
- *           use the default
- * @mode: cache mode of the @pool_name cache pool
- * @flags: a combination of (ORed) #BDLVMCachePoolFlags
- * @fast_pvs: (array zero-terminated=1): list of (fast) PVs to create the @pool_name
- *                                       cache pool (and the metadata LV)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cache pool @vg_name/@pool_name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_cache_create_pool (const gchar *vg_name, const gchar *pool_name, guint64 pool_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags, const gchar **fast_pvs, GError **error) {
-    gboolean success = FALSE;
-    const gchar *type = NULL;
-    gchar *name = NULL;
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    GVariant *extra = NULL;
-    gchar *lv_id = NULL;
-    gchar *lv_obj_path = NULL;
-    const gchar *mode_str = NULL;
-    gchar *msg = NULL;
-    guint64 progress_id = 0;
-    GError *l_error = NULL;
-
-    msg = g_strdup_printf ("Started 'create cache pool %s/%s'", vg_name, pool_name);
-    progress_id = bd_utils_report_started (msg);
-    g_free (msg);
-
-    /* create an LV for the pool */
-    type = get_lv_type_from_flags (flags, FALSE, NULL);
-    success = bd_lvm_lvcreate (vg_name, pool_name, pool_size, type, fast_pvs, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the pool LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 1/3 steps done */
-    bd_utils_report_progress (progress_id, 33, "Created the data LV");
-
-    /* determine the size of the metadata LV */
-    type = get_lv_type_from_flags (flags, TRUE, NULL);
-    if (md_size == 0)
-        md_size = bd_lvm_cache_get_default_md_size (pool_size, NULL);
-    name = g_strdup_printf ("%s_meta", pool_name);
-
-    /* create the metadata LV */
-    success = bd_lvm_lvcreate (vg_name, name, md_size, type, fast_pvs, NULL, &l_error);
-    if (!success) {
-        g_free (name);
-        g_prefix_error (&l_error, "Failed to create the pool metadata LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 2/3 steps done */
-    bd_utils_report_progress (progress_id, 66, "Created the metadata LV");
-
-    /* create the cache pool from the two LVs */
-    /* build the params tuple */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    lv_id = g_strdup_printf ("%s/%s", vg_name, name);
-    lv_obj_path = get_object_path (lv_id, &l_error);
-    g_free (lv_id);
-    if (!lv_obj_path) {
-        g_variant_builder_clear (&builder);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
-    lv_id = g_strdup_printf ("%s/%s", vg_name, pool_name);
-    lv_obj_path = get_object_path (lv_id, &l_error);
-    g_free (lv_id);
-    if (!lv_obj_path) {
-        g_variant_builder_clear (&builder);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    /* build the dictionary with the extra params */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-    mode_str = bd_lvm_cache_get_mode_str (mode, &l_error);
-    if (!mode_str) {
-        g_variant_builder_clear (&builder);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-    g_variant_builder_add (&builder, "{sv}", "cachemode", g_variant_new ("s", mode_str));
-    extra = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    success = call_lvm_obj_method_sync (vg_name, VG_INTF, "CreateCachePool", params, extra, NULL, TRUE, &l_error);
-    if (!success) {
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-    } else
-        bd_utils_report_finished (progress_id, "Completed");
-
-    return success;
-}
-
-/**
- * bd_lvm_cache_attach:
- * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
- * @data_lv: data LV to attach the @cache_pool_lv to
- * @cache_pool_lv: cache pool LV to attach to the @data_lv
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @cache_pool_lv was successfully attached to the @data_lv or not
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_cache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_pool_lv, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    gchar *lv_id = NULL;
-    g_autofree gchar *lv_obj_path = NULL;
-    gboolean ret = FALSE;
-
-    lv_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
-    lv_obj_path = get_object_path (lv_id, error);
-    g_free (lv_id);
-    if (!lv_obj_path)
-        return FALSE;
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    lv_id = g_strdup_printf ("%s/%s", vg_name, cache_pool_lv);
-
-    ret = call_lvm_obj_method_sync (lv_id, CACHE_POOL_INTF, "CacheLv", params, NULL, extra, TRUE, error);
-    g_free (lv_id);
-    return ret;
-}
-
-/**
- * bd_lvm_cache_detach:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: name of the cached LV to detach its cache from
- * @destroy: whether to destroy the cache after detach or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cache was successfully detached from the @cached_lv or not
- *
- * Note: synces the cache first
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_cache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
-    g_autofree gchar *lv_id = NULL;
-    g_autofree gchar *cache_pool_name = NULL;
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("b", destroy));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    cache_pool_name = bd_lvm_cache_pool_name (vg_name, cached_lv, error);
-    if (!cache_pool_name)
-        return FALSE;
-    lv_id = g_strdup_printf ("%s/%s", vg_name, cached_lv);
-    return call_lvm_obj_method_sync (lv_id, CACHED_LV_INTF, "DetachCachePool", params, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_cache_create_cached_lv:
- * @vg_name: name of the VG to create a cached LV in
- * @lv_name: name of the cached LV to create
- * @data_size: size of the data LV
- * @cache_size: size of the cache (or cached LV more precisely)
- * @md_size: size of the cache metadata LV or 0 to use the default
- * @mode: cache mode for the cached LV
- * @flags: a combination of (ORed) #BDLVMCachePoolFlags
- * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
- * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cached LV @lv_name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_cache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags,
-                                        const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
-    gboolean success = FALSE;
-    gchar *name = NULL;
-    gchar *msg = NULL;
-    guint64 progress_id = 0;
-    GError *l_error = NULL;
-
-    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
-    progress_id = bd_utils_report_started (msg);
-    g_free (msg);
-
-    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the data LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 1/5 steps (cache pool creation has 3 steps) done */
-    bd_utils_report_progress (progress_id, 20, "Data LV created");
-
-    name = g_strdup_printf ("%s_cache", lv_name);
-    success = bd_lvm_cache_create_pool (vg_name, name, cache_size, md_size, mode, flags, fast_pvs, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the cache pool '%s': ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 4/5 steps (cache pool creation has 3 steps) done */
-    bd_utils_report_progress (progress_id, 80, "Cache pool created");
-
-    success = bd_lvm_cache_attach (vg_name, lv_name, name, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to attach the cache pool '%s' to the data LV: ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    bd_utils_report_finished (progress_id, "Completed");
-    g_free (name);
-    return TRUE;
-}
-
-/**
- * bd_lvm_writecache_attach:
- * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
- * @data_lv: data LV to attach the @cache_lv to
- * @cache_lv: cache (fast) LV to attach to the @data_lv
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @cache_lv was successfully attached to the @data_lv or not
- *
- * Note: Both @data_lv and @cache_lv will be deactivated before the operation.
- *
- * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_writecache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_lv, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    gchar *lv_id = NULL;
-    g_autofree gchar *lv_obj_path = NULL;
-    gboolean success = FALSE;
-
-    /* both LVs need to be inactive for the writecache convert to work */
-    success = bd_lvm_lvdeactivate (vg_name, data_lv, NULL, error);
-    if (!success)
-        return FALSE;
-
-    success = bd_lvm_lvdeactivate (vg_name, cache_lv, NULL, error);
-    if (!success)
-        return FALSE;
-
-    lv_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
-    lv_obj_path = get_object_path (lv_id, error);
-    g_free (lv_id);
-    if (!lv_obj_path)
-        return FALSE;
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("o", lv_obj_path));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    lv_id = g_strdup_printf ("%s/%s", vg_name, cache_lv);
-
-    success = call_lvm_obj_method_sync (lv_id, LV_INTF, "WriteCacheLv", params, NULL, extra, TRUE, error);
-    g_free (lv_id);
-    return success;
-}
-
-/**
- * bd_lvm_writecache_detach:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: name of the cached LV to detach its cache from
- * @destroy: whether to destroy the cache after detach or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cache was successfully detached from the @cached_lv or not
- *
- * Note: synces the cache first
- *
- * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_writecache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
-    return bd_lvm_cache_detach (vg_name, cached_lv, destroy, extra, error);
-}
-
-/**
- * bd_lvm_writecache_create_cached_lv:
- * @vg_name: name of the VG to create a cached LV in
- * @lv_name: name of the cached LV to create
- * @data_size: size of the data LV
- * @cache_size: size of the cache (or cached LV more precisely)
- * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
- * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cached LV @lv_name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_writecache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size,
-                                             const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
-    gboolean success = FALSE;
-    gchar *name = NULL;
-    gchar *msg = NULL;
-    guint64 progress_id = 0;
-    GError *l_error = NULL;
-
-    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
-    progress_id = bd_utils_report_started (msg);
-    g_free (msg);
-
-    name = g_strdup_printf ("%s_writecache", lv_name);
-    success = bd_lvm_lvcreate (vg_name, name, cache_size, NULL, fast_pvs, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the cache LV '%s': ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 1/3 steps done */
-    bd_utils_report_progress (progress_id, 33, "Cache LV created");
-
-    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the data LV: ");
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 2/3 steps done */
-    bd_utils_report_progress (progress_id, 66, "Data LV created");
-
-    success = bd_lvm_writecache_attach (vg_name, lv_name, name, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to attach the cache LV '%s' to the data LV: ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    bd_utils_report_finished (progress_id, "Completed");
-    g_free (name);
-    return TRUE;
-}
-
-/**
- * bd_lvm_cache_pool_name:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: cached LV to get the name of the its pool LV for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: name of the cache pool LV used by the @cached_lv or %NULL in case of error
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_cache_pool_name (const gchar *vg_name, const gchar *cached_lv, GError **error) {
-    gchar *ret = NULL;
-    gchar *name_start = NULL;
-    gchar *name_end = NULL;
-    gchar *pool_name = NULL;
-    gchar *lv_spec = NULL;
-    GVariant *prop = NULL;
-    gchar *pool_obj_path = NULL;
-
-    /* same as for a thin LV, but with square brackets */
-    lv_spec = g_strdup_printf ("%s/%s", vg_name, cached_lv);
-    prop = get_lvm_object_property (lv_spec, CACHED_LV_INTF, "CachePool", error);
-    g_free (lv_spec);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "o", &pool_obj_path);
-    prop = get_object_property (pool_obj_path, LV_CMN_INTF, "Name", error);
-    g_free (pool_obj_path);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "s", &ret);
-    g_variant_unref (prop);
-
-    name_start = strchr (ret, '[');
-    if (!name_start) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Failed to determine cache pool name from: '%s'", ret);
-        g_free (ret);
-        return NULL;
-    }
-    name_start++;
-
-    name_end = strchr (ret, ']');
-    if (!name_end) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Failed to determine cache pool name from: '%s'", ret);
-        g_free (ret);
-        return NULL;
-    }
-
-    pool_name = g_strndup (name_start, name_end - name_start);
-    g_free (ret);
-
-    return pool_name;
-}
-
-/**
- * bd_lvm_cache_stats:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: cached LV to get stats for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: stats for the @cached_lv or %NULL in case of error
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error) {
-    struct dm_pool *pool = NULL;
-    struct dm_task *task = NULL;
-    struct dm_info info;
-    struct dm_status_cache *status = NULL;
-    gchar *map_name = NULL;
-    guint64 start = 0;
-    guint64 length = 0;
-    gchar *type = NULL;
-    gchar *params = NULL;
-    BDLVMCacheStats *ret = NULL;
-    BDLVMLVdata *lvdata = NULL;
-
-    if (geteuid () != 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_ROOT,
-                     "Not running as root, cannot query DM maps");
-        return NULL;
-    }
-
-    lvdata = bd_lvm_lvinfo (vg_name, cached_lv, error);
-    if (!lvdata)
-        return NULL;
-
-    pool = dm_pool_create ("bd-pool", 20);
-
-    if (g_strcmp0 (lvdata->segtype, "thin-pool") == 0)
-        map_name = dm_build_dm_name (pool, vg_name, lvdata->data_lv, NULL);
-    else
-        /* translate the VG+LV name into the DM map name */
-        map_name = dm_build_dm_name (pool, vg_name, cached_lv, NULL);
-
-    bd_lvm_lvdata_free (lvdata);
-
-    task = dm_task_create (DM_DEVICE_STATUS);
-    if (!task) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to create DM task for the cache map '%s': ", map_name);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (dm_task_set_name (task, map_name) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to create DM task for the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (dm_task_run (task) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to run the DM task for the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (dm_task_get_info (task, &info) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to get task info for the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (!info.exists) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_NOCACHE,
-                     "The cache map '%s' doesn't exist: ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    dm_get_next_target (task, NULL, &start, &length, &type, &params);
-
-    if (dm_get_status_cache (pool, params, &status) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Failed to get status of the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    ret = g_new0 (BDLVMCacheStats, 1);
-    ret->block_size = status->block_size * SECTOR_SIZE;
-    ret->cache_size = status->total_blocks * ret->block_size;
-    ret->cache_used = status->used_blocks * ret->block_size;
-
-    ret->md_block_size = status->metadata_block_size * SECTOR_SIZE;
-    ret->md_size = status->metadata_total_blocks * ret->md_block_size;
-    ret->md_used = status->metadata_used_blocks * ret->md_block_size;
-
-    ret->read_hits = status->read_hits;
-    ret->read_misses = status->read_misses;
-    ret->write_hits = status->write_hits;
-    ret->write_misses = status->write_misses;
-
-    if (status->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH)
-        ret->mode = BD_LVM_CACHE_MODE_WRITETHROUGH;
-    else if (status->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
-        ret->mode = BD_LVM_CACHE_MODE_WRITEBACK;
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                      "Failed to determine status of the cache from '%"G_GUINT64_FORMAT"': ",
-                      status->feature_flags);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        bd_lvm_cache_stats_free (ret);
-        return NULL;
-    }
-
-    dm_task_destroy (task);
-    dm_pool_destroy (pool);
-
-    return ret;
-}
-
-/**
- * bd_lvm_thpool_convert:
- * @vg_name: name of the VG to create the new thin pool in
- * @data_lv: name of the LV that should become the data part of the new pool
- * @metadata_lv: name of the LV that should become the metadata part of the new pool
- * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Converts the @data_lv and @metadata_lv into a new thin pool in the @vg_name
- * VG.
- *
- * Returns: whether the new thin pool was successfully created from @data_lv and
- *          @metadata_lv or not
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thpool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    gchar *obj_id = NULL;
-    gchar *data_lv_path = NULL;
-    gchar *metadata_lv_path = NULL;
-    gboolean ret = FALSE;
-
-    obj_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
-    data_lv_path = get_object_path (obj_id, error);
-    g_free (obj_id);
-    if (!data_lv_path)
-        return FALSE;
-
-    obj_id = g_strdup_printf ("%s/%s", vg_name, metadata_lv);
-    metadata_lv_path = get_object_path (obj_id, error);
-    g_free (obj_id);
-    if (!metadata_lv_path)
-        return FALSE;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("o", metadata_lv_path));
-    g_variant_builder_add_value (&builder, g_variant_new ("o", data_lv_path));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    ret = call_lvm_obj_method_sync (vg_name, VG_INTF, "CreateThinPool", params, NULL, extra, TRUE, error);
-    if (ret && name)
-        bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
-    return ret;
-}
-
-/**
- * bd_lvm_cache_pool_convert:
- * @vg_name: name of the VG to create the new thin pool in
- * @data_lv: name of the LV that should become the data part of the new pool
- * @metadata_lv: name of the LV that should become the metadata part of the new pool
- * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Converts the @data_lv and @metadata_lv into a new cache pool in the @vg_name
- * VG.
- *
- * Returns: whether the new cache pool was successfully created from @data_lv and
- *          @metadata_lv or not
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_cache_pool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    gchar *obj_id = NULL;
-    gchar *data_lv_path = NULL;
-    gchar *metadata_lv_path = NULL;
-    gboolean ret = FALSE;
-
-    obj_id = g_strdup_printf ("%s/%s", vg_name, data_lv);
-    data_lv_path = get_object_path (obj_id, error);
-    g_free (obj_id);
-    if (!data_lv_path)
-        return FALSE;
-
-    obj_id = g_strdup_printf ("%s/%s", vg_name, metadata_lv);
-    metadata_lv_path = get_object_path (obj_id, error);
-    g_free (obj_id);
-    if (!metadata_lv_path)
-        return FALSE;
-
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-    g_variant_builder_add_value (&builder, g_variant_new ("o", metadata_lv_path));
-    g_variant_builder_add_value (&builder, g_variant_new ("o", data_lv_path));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    ret = call_lvm_obj_method_sync (vg_name, VG_INTF, "CreateCachePool", params, NULL, extra, TRUE, error);
-
-    if (!ret && name)
-        bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
-
-    return ret;
-}
-
-/**
- * bd_lvm_vdo_pool_create:
- * @vg_name: name of the VG to create a new LV in
- * @lv_name: name of the to-be-created VDO LV
- * @pool_name: (nullable): name of the to-be-created VDO pool LV or %NULL for default name
- * @data_size: requested size of the data VDO LV (physical size of the @pool_name VDO pool LV)
- * @virtual_size: requested virtual_size of the @lv_name VDO LV
- * @index_memory: amount of index memory (in bytes) or 0 for default
- * @compression: whether to enable compression or not
- * @deduplication: whether to enable deduplication or not
- * @write_policy: write policy for the volume
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given @vg_name/@lv_name VDO LV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_vdo_pool_create (const gchar *vg_name, const gchar *lv_name, const gchar *pool_name, guint64 data_size, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
-    GVariantBuilder builder;
-    GVariant *params = NULL;
-    GVariant *extra_params = NULL;
-    gchar *old_config = NULL;
-    const gchar *write_policy_str = NULL;
-    g_autofree gchar *name = NULL;
-    gboolean ret = FALSE;
-
-    write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
-    if (write_policy_str == NULL)
-        return FALSE;
-
-    /* build the params tuple */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-
-    if (!pool_name) {
-        name = g_strdup_printf ("%s_vpool", lv_name);
-        g_variant_builder_add_value (&builder, g_variant_new ("s", name));
-    } else
-        g_variant_builder_add_value (&builder, g_variant_new ("s", pool_name));
-
-    g_variant_builder_add_value (&builder, g_variant_new ("s", lv_name));
-    g_variant_builder_add_value (&builder, g_variant_new ("t", data_size));
-    g_variant_builder_add_value (&builder, g_variant_new ("t", virtual_size));
-    params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    /* and now the extra_params params */
-    g_variant_builder_init (&builder, G_VARIANT_TYPE_DICTIONARY);
-    g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--compression", g_variant_new ("s", compression ? "y" : "n")));
-    g_variant_builder_add_value (&builder, g_variant_new ("{sv}", "--deduplication", g_variant_new ("s", deduplication ? "y" : "n")));
-    extra_params = g_variant_builder_end (&builder);
-    g_variant_builder_clear (&builder);
-
-    /* index_memory and write_policy can be specified only using the config */
-    g_mutex_lock (&global_config_lock);
-    old_config = global_config_str;
-    if (index_memory != 0)
-        global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
-                                                                                                                                     index_memory / (1024 * 1024),
-                                                                                                                                     write_policy_str);
-    else
-        global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
-                                                                                        write_policy_str);
-
-    ret = call_lvm_obj_method_sync (vg_name, VG_VDO_INTF, "CreateVdoPoolandLv", params, extra_params, extra, FALSE, error);
-
-    g_free (global_config_str);
-    global_config_str = old_config;
-    g_mutex_unlock (&global_config_lock);
-
-    return ret;
-}
-
-/**
- * bd_lvm_vdo_enable_compression:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to enable compression on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether compression was successfully enabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_enable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return call_vdopool_method_sync (vg_name, pool_name, "EnableCompression", NULL, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vdo_disable_compression:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to disable compression on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether compression was successfully disabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_disable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return call_vdopool_method_sync (vg_name, pool_name, "DisableCompression", NULL, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vdo_enable_deduplication:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to enable deduplication on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether deduplication was successfully enabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_enable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return call_vdopool_method_sync (vg_name, pool_name, "EnableDeduplication", NULL, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vdo_enable_deduplication:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to disable deduplication on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether deduplication was successfully disabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_disable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return call_vdopool_method_sync (vg_name, pool_name, "DisableDeduplication", NULL, NULL, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vdo_info:
- * @vg_name: name of the VG that contains the LV to get information about
- * @pool_name: name of the VDO pool LV to get information about
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
- * of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVDOPooldata* bd_lvm_vdo_info (const gchar *vg_name, const gchar *pool_name, GError **error) {
-    GVariant *props = NULL;
-
-    props = get_vdo_properties (vg_name, pool_name, error);
-    if (!props)
-        /* the error is already populated */
-        return NULL;
-
-    return get_vdo_data_from_props (props, error);
-}
-
-/**
- * bd_lvm_vdo_resize:
- * @vg_name: name of the VG containing the to-be-resized VDO LV
- * @lv_name: name of the to-be-resized VDO LV
- * @size: the requested new size of the VDO LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name VDO LV was successfully resized or not
- *
- * Note: Reduction needs to process TRIM for reduced disk area to unmap used data blocks
- *       from the VDO pool LV and it may take a long time.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_resize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    return bd_lvm_lvresize (vg_name, lv_name, size, extra, error);
-}
-
-/**
- * bd_lvm_vdo_pool_resize:
- * @vg_name: name of the VG containing the to-be-resized VDO pool LV
- * @pool_name: name of the to-be-resized VDO pool LV
- * @size: the requested new size of the VDO pool LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool LV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@pool_name VDO pool LV was successfully resized or not
- *
- * Note: Size of the VDO pool LV can be only extended, not reduced.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_pool_resize (const gchar *vg_name, const gchar *pool_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    BDLVMLVdata *info = NULL;
-
-    info = bd_lvm_lvinfo (vg_name, pool_name, error);
-    if (!info)
-        return FALSE;
-
-    if (info->size >= size) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_SUPPORTED,
-                     "Reducing physical size of the VDO pool LV is not supported.");
-        bd_lvm_lvdata_free (info);
-        return FALSE;
-    }
-
-    bd_lvm_lvdata_free (info);
-
-    return bd_lvm_lvresize (vg_name, pool_name, size, extra, error);
-}
-
-/**
- * bd_lvm_vdo_pool_convert:
- * @vg_name: name of the VG that contains @pool_lv
- * @pool_lv: name of the LV that should become the new VDO pool LV
- * @name: (nullable): name for the VDO LV or %NULL for default name
- * @virtual_size: virtual size for the new VDO LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Converts the @pool_lv into a new VDO pool LV in the @vg_name VG and creates a new
- * @name VDO LV with size @virtual_size.
- *
- * Note: All data on @pool_lv will be irreversibly destroyed.
- *
- * Returns: whether the new VDO pool LV was successfully created from @pool_lv and or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE&%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_pool_convert (const gchar *vg_name G_GNUC_UNUSED, const gchar *pool_lv G_GNUC_UNUSED, const gchar *name G_GNUC_UNUSED,
-                                  guint64 virtual_size G_GNUC_UNUSED, guint64 index_memory G_GNUC_UNUSED, gboolean compression G_GNUC_UNUSED,
-                                  gboolean deduplication G_GNUC_UNUSED, BDLVMVDOWritePolicy write_policy G_GNUC_UNUSED,
-                                  const BDExtraArg **extra G_GNUC_UNUSED, GError **error) {
-    return bd_lvm_is_tech_avail (BD_LVM_TECH_VDO, BD_LVM_TECH_MODE_CREATE | BD_LVM_TECH_MODE_MODIFY, error);
-}
-
-/**
- * bd_lvm_vdolvpoolname:
- * @vg_name: name of the VG containing the queried VDO LV
- * @lv_name: name of the queried VDO LV
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
- * VDO LV or %NULL if failed to determine (@error) is set in those cases
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_vdolvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    GVariant *prop = NULL;
-    const gchar *segtype = NULL;
-    gchar *pool_obj_path = NULL;
-    gchar *ret = NULL;
-
-    prop = get_lv_property (vg_name, lv_name, "SegType", error);
-    if (!prop)
-        return NULL;
-
-    g_variant_get_child (prop, 0, "&s", &segtype);
-    if (g_strcmp0 (segtype, "vdo") != 0) {
-        g_variant_unref (prop);
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOEXIST,
-                     "The LV '%s' is not a VDO LV and thus have no VDO pool", lv_name);
-        return NULL;
-    }
-    g_variant_unref (prop);
-
-    prop = get_lv_property (vg_name, lv_name, "PoolLv", error);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "o", &pool_obj_path);
-    g_variant_unref (prop);
-
-    prop = get_object_property (pool_obj_path, LV_CMN_INTF, "Name", error);
-    g_free (pool_obj_path);
-    if (!prop)
-        return NULL;
-    g_variant_get (prop, "s", &ret);
-    g_variant_unref (prop);
-
-    return ret;
-}
-
-/**
- * bd_lvm_get_vdo_operating_mode_str:
- * @mode: mode to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @mode or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_operating_mode_str (BDLVMVDOOperatingMode mode, GError **error) {
-    switch (mode) {
-    case BD_LVM_VDO_MODE_RECOVERING:
-        return "recovering";
-    case BD_LVM_VDO_MODE_READ_ONLY:
-        return "read-only";
-    case BD_LVM_VDO_MODE_NORMAL:
-        return "normal";
-    case BD_LVM_VDO_MODE_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO operating mode.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_compression_state_str:
- * @state: state to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @state or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_compression_state_str (BDLVMVDOCompressionState state, GError **error) {
-    switch (state) {
-    case BD_LVM_VDO_COMPRESSION_ONLINE:
-        return "online";
-    case BD_LVM_VDO_COMPRESSION_OFFLINE:
-        return "offline";
-    case BD_LVM_VDO_COMPRESSION_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO compression state.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_index_state_str:
- * @state: state to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @state or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_index_state_str (BDLVMVDOIndexState state, GError **error) {
-    switch (state) {
-    case BD_LVM_VDO_INDEX_ERROR:
-        return "error";
-    case BD_LVM_VDO_INDEX_CLOSED:
-        return "closed";
-    case BD_LVM_VDO_INDEX_OPENING:
-        return "opening";
-    case BD_LVM_VDO_INDEX_CLOSING:
-        return "closing";
-    case BD_LVM_VDO_INDEX_OFFLINE:
-        return "offline";
-    case BD_LVM_VDO_INDEX_ONLINE:
-        return "online";
-    case BD_LVM_VDO_INDEX_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO index state.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_write_policy_str:
- * @policy: policy to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @policy or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_write_policy_str (BDLVMVDOWritePolicy policy, GError **error) {
-    switch (policy) {
-    case BD_LVM_VDO_WRITE_POLICY_AUTO:
-        return "auto";
-    case BD_LVM_VDO_WRITE_POLICY_SYNC:
-        return "sync";
-    case BD_LVM_VDO_WRITE_POLICY_ASYNC:
-        return "async";
-    case BD_LVM_VDO_WRITE_POLICY_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO write policy.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_write_policy_from_str:
- * @policy_str: string representation of a policy
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: write policy for the @policy_str or %BD_LVM_VDO_WRITE_POLICY_UNKNOWN if
- *          failed to determine
- *
- * Tech category: always provided/supported
- */
-BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_str, GError **error) {
-    if (g_strcmp0 (policy_str, "auto") == 0)
-        return BD_LVM_VDO_WRITE_POLICY_AUTO;
-    else if (g_strcmp0 (policy_str, "sync") == 0)
-        return BD_LVM_VDO_WRITE_POLICY_SYNC;
-    else if (g_strcmp0 (policy_str, "async") == 0)
-        return BD_LVM_VDO_WRITE_POLICY_ASYNC;
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_VDO_POLICY_INVAL,
-                     "Invalid policy given: %s", policy_str);
-        return BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
-    }
-}
-
-/**
- * bd_lvm_vdo_get_stats_full:
- * @vg_name: name of the VG that contains @pool_name VDO pool
- * @pool_name: name of the VDO pool to get statistics for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full) (element-type utf8 utf8): hashtable of type string - string of available
- *                                                    statistics or %NULL in case of error
- *                                                    (@error gets populated in those cases)
- *
- * Statistics are collected from the values exposed by the kernel `dm-vdo` module.
- *
- * Some of the keys are computed to mimic the information produced by the vdo tools.
- * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error) {
-    g_autofree gchar *kvdo_name = g_strdup_printf ("%s-%s-%s", vg_name, pool_name, VDO_POOL_SUFFIX);
-    return vdo_get_stats_full (kvdo_name, error);
-}
-
-/**
- * bd_lvm_vdo_get_stats:
- * @vg_name: name of the VG that contains @pool_name VDO pool
- * @pool_name: name of the VDO pool to get statistics for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): a structure containing selected statistics or %NULL in case of error
- *                           (@error gets populated in those cases)
- *
- * In contrast to @bd_lvm_vdo_get_stats_full this function will only return selected statistics
- * in a fixed structure. In case a value is not available, -1 would be returned.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error) {
-    GHashTable *full_stats = NULL;
-    BDLVMVDOStats *stats = NULL;
-
-    full_stats = bd_lvm_vdo_get_stats_full (vg_name, pool_name, error);
-    if (!full_stats)
-        return NULL;
-
-    stats = g_new0 (BDLVMVDOStats, 1);
-    get_stat_val64_default (full_stats, "blockSize", &stats->block_size, -1);
-    get_stat_val64_default (full_stats, "logicalBlockSize", &stats->logical_block_size, -1);
-    get_stat_val64_default (full_stats, "physicalBlocks", &stats->physical_blocks, -1);
-    get_stat_val64_default (full_stats, "dataBlocksUsed", &stats->data_blocks_used, -1);
-    get_stat_val64_default (full_stats, "overheadBlocksUsed", &stats->overhead_blocks_used, -1);
-    get_stat_val64_default (full_stats, "logicalBlocksUsed", &stats->logical_blocks_used, -1);
-    get_stat_val64_default (full_stats, "usedPercent", &stats->used_percent, -1);
-    get_stat_val64_default (full_stats, "savingPercent", &stats->saving_percent, -1);
-    if (!get_stat_val_double (full_stats, "writeAmplificationRatio", &stats->write_amplification_ratio))
-        stats->write_amplification_ratio = -1;
-
-    g_hash_table_destroy (full_stats);
-
-    return stats;
-}
-
-/* check whether the LVM devices file is enabled by LVM
- * we use the existence of the "lvmdevices" command to check whether the feature is available
- * or not, but this can still be disabled either in LVM or in lvm.conf
- */
-static gboolean _lvm_devices_enabled () {
-    const gchar *args[6] = {"lvmconfig", "--typeconfig", NULL, "devices/use_devicesfile", NULL, NULL};
-    gboolean ret = FALSE;
-    GError *loc_error = NULL;
-    gchar *output = NULL;
-    gboolean enabled = FALSE;
-    gint scanned = 0;
-    g_autofree gchar *config_arg = NULL;
-
-    /* try full config first -- if we get something from this it means the feature is
-       explicitly enabled or disabled by system lvm.conf or using the --config option */
-    args[2] = "full";
-
-    /* make sure to include the global config from us when getting the current config value */
-    g_mutex_lock (&global_config_lock);
-    if (global_config_str) {
-        config_arg = g_strdup_printf ("--config=%s", global_config_str);
-        args[4] = config_arg;
-    }
-
-    ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
-    g_mutex_unlock (&global_config_lock);
-    if (ret) {
-        scanned = sscanf (output, "use_devicesfile=%u", &enabled);
-        g_free (output);
-        if (scanned != 1)
-            return FALSE;
-
-        return enabled;
-    } else {
-        g_clear_error (&loc_error);
-        g_free (output);
-    }
-
-    output = NULL;
-
-    /* now try default */
-    args[2] = "default";
-    ret = bd_utils_exec_and_capture_output (args, NULL, &output, &loc_error);
-    if (ret) {
-        scanned = sscanf (output, "# use_devicesfile=%u", &enabled);
-        g_free (output);
-        if (scanned != 1)
-            return FALSE;
-
-        return enabled;
-    } else {
-        g_clear_error (&loc_error);
-        g_free (output);
-    }
-
-    return FALSE;
-}
-
-/**
- * bd_lvm_devices_add:
- * @device: device (PV) to add to the devices file
- * @devices_file: (nullable): LVM devices file or %NULL for default
- * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @device was successfully added to @devices_file or not
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"lvmdevices", "--adddev", device, NULL, NULL};
-    g_autofree gchar *devfile = NULL;
-
-    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
-        return FALSE;
-
-    if (!_lvm_devices_enabled ()) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
-                     "LVM devices file not enabled.");
-        return FALSE;
-    }
-
-    if (devices_file) {
-        devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
-        args[3] = devfile;
-    }
-
-    return bd_utils_exec_and_report_error (args, extra, error);
-}
-
-/**
- * bd_lvm_devices_delete:
- * @device: device (PV) to delete from the devices file
- * @devices_file: (nullable): LVM devices file or %NULL for default
- * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @device was successfully removed from @devices_file or not
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"lvmdevices", "--deldev", device, NULL, NULL};
-    g_autofree gchar *devfile = NULL;
-
-    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
-        return FALSE;
-
-    if (!_lvm_devices_enabled ()) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
-                     "LVM devices file not enabled.");
-        return FALSE;
-    }
-
-    if (devices_file) {
-        devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
-        args[3] = devfile;
-    }
-
-    return bd_utils_exec_and_report_error (args, extra, error);
-}
-
-/**
- * bd_lvm_config_get:
- * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config
- * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section
- * @type: type of the config, e.g. 'full' or 'current'
- * @values_only: whether to include only values without keys in the output
- * @global_config: whether to include our internal global config in the call or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command
- *                                               (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: Requested LVM config @section and @setting configuration or %NULL in case of error.
- *
- * Tech category: (transfer full): %BD_LVM_TECH_CONFIG no mode (it is ignored)
- */
-gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error) {
-    g_autofree gchar *conf_spec = NULL;
-    g_autofree gchar *config_arg = NULL;
-    const gchar *args[7] = {"lvmconfig", "--typeconfig", NULL, NULL, NULL, NULL, NULL};
-    guint next_arg = 2;
-    gchar *output = NULL;
-    gboolean success = FALSE;
-
-    if (!section && setting) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Specifying setting without section is not supported.");
-        return NULL;
-    }
-
-    if (section)
-        if (setting)
-            conf_spec = g_strdup_printf ("%s/%s", section, setting);
-        else
-            conf_spec = g_strdup (section);
-    else
-        conf_spec = NULL;
-
-    args[next_arg++] = type;
-    args[next_arg++] = conf_spec;
-    if (values_only)
-        args[next_arg++] = "--valuesonly";
-
-    g_mutex_lock (&global_config_lock);
-    if (global_config && global_config_str) {
-        config_arg = g_strdup_printf ("--config=%s", global_config_str);
-        args[next_arg++] = config_arg;
-    }
-    g_mutex_unlock (&global_config_lock);
-
-    success = bd_utils_exec_and_capture_output (args, extra, &output, error);
-    if (!success)
-        return NULL;
-    return g_strchomp (output);
-}
diff -pruN 3.3.1-3/src/plugins/lvm.c 3.4.0-1/src/plugins/lvm.c
--- 3.3.1-3/src/plugins/lvm.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,4032 +0,0 @@
-/*
- * Copyright (C) 2014  Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Vratislav Podzimek <vpodzime@redhat.com>
- */
-
-#include <glib.h>
-#include <math.h>
-#include <string.h>
-#include <unistd.h>
-#include <blockdev/utils.h>
-#include <libdevmapper.h>
-
-#include "lvm.h"
-#include "check_deps.h"
-#include "dm_logging.h"
-#include "vdo_stats.h"
-
-#define INT_FLOAT_EPS 1e-5
-#define SECTOR_SIZE 512
-#define VDO_POOL_SUFFIX "vpool"
-#define DEFAULT_PE_SIZE (4 MiB)
-#define USE_DEFAULT_PE_SIZE 0
-#define RESOLVE_PE_SIZE(size) ((size) == USE_DEFAULT_PE_SIZE ? DEFAULT_PE_SIZE : (size))
-#define THPOOL_MD_FACTOR_NEW (0.2)
-#define THPOOL_MD_FACTOR_EXISTS (1 / 6.0)
-
-#define MIN_PE_SIZE (1 KiB)
-#define MAX_PE_SIZE (16 GiB)
-
-#define MIN_THPOOL_MD_SIZE (4 MiB)
-/* DM_THIN_MAX_METADATA_SIZE is in 512 sectors */
-#define MAX_THPOOL_MD_SIZE (DM_THIN_MAX_METADATA_SIZE * 512)
-
-#define MIN_THPOOL_CHUNK_SIZE (64 KiB)
-#define MAX_THPOOL_CHUNK_SIZE (1 GiB)
-#define DEFAULT_CHUNK_SIZE (64 KiB)
-
-/* according to lvmcache (7) */
-#define MIN_CACHE_MD_SIZE (8 MiB)
-
-#ifdef __LP64__
-/* 64bit system */
-#define MAX_LV_SIZE (8 EiB)
-#else
-/* 32bit system */
-#define MAX_LV_SIZE (16 TiB)
-#endif
-
-#define LVM_MIN_VERSION "2.02.116"
-#define LVM_VERSION_FSRESIZE "2.03.19"
-
-static GMutex global_config_lock;
-static gchar *global_config_str = NULL;
-
-static gchar *global_devices_str = NULL;
-
-/**
- * SECTION: lvm
- * @short_description: plugin for operations with LVM
- * @title: LVM
- * @include: lvm.h
- *
- * A plugin for operations with LVM. All sizes passed in/out to/from
- * the functions are in bytes.
- */
-
-/**
- * bd_lvm_error_quark: (skip)
- */
-GQuark bd_lvm_error_quark (void)
-{
-    return g_quark_from_static_string ("g-bd-lvm-error-quark");
-}
-
-BDLVMPVdata* bd_lvm_pvdata_copy (BDLVMPVdata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMPVdata *new_data = g_new0 (BDLVMPVdata, 1);
-
-    new_data->pv_name = g_strdup (data->pv_name);
-    new_data->pv_uuid = g_strdup (data->pv_uuid);
-    new_data->pv_free = data->pv_free;
-    new_data->pv_size = data->pv_size;
-    new_data->pe_start = data->pe_start;
-    new_data->vg_name = g_strdup (data->vg_name);
-    new_data->vg_uuid = g_strdup (data->vg_uuid);
-    new_data->vg_size = data->vg_size;
-    new_data->vg_free = data->vg_free;
-    new_data->vg_extent_size = data->vg_extent_size;
-    new_data->vg_extent_count = data->vg_extent_count;
-    new_data->vg_free_count = data->vg_free_count;
-    new_data->vg_pv_count = data->vg_pv_count;
-    new_data->pv_tags = g_strdupv (data->pv_tags);
-    new_data->missing = data->missing;
-
-    return new_data;
-}
-
-void bd_lvm_pvdata_free (BDLVMPVdata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data->pv_name);
-    g_free (data->pv_uuid);
-    g_free (data->vg_name);
-    g_free (data->vg_uuid);
-    g_strfreev (data->pv_tags);
-    g_free (data);
-}
-
-BDLVMVGdata* bd_lvm_vgdata_copy (BDLVMVGdata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMVGdata *new_data = g_new0 (BDLVMVGdata, 1);
-
-    new_data->name = g_strdup (data->name);
-    new_data->uuid = g_strdup (data->uuid);
-    new_data->size = data->size;
-    new_data->free = data->free;
-    new_data->extent_size = data->extent_size;
-    new_data->extent_count = data->extent_count;
-    new_data->free_count = data->free_count;
-    new_data->pv_count = data->pv_count;
-    new_data->vg_tags = g_strdupv (data->vg_tags);
-    return new_data;
-}
-
-void bd_lvm_vgdata_free (BDLVMVGdata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data->name);
-    g_free (data->uuid);
-    g_strfreev (data->vg_tags);
-    g_free (data);
-}
-
-BDLVMSEGdata* bd_lvm_segdata_copy (BDLVMSEGdata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMSEGdata *new_data = g_new0 (BDLVMSEGdata, 1);
-
-    new_data->size_pe = data->size_pe;
-    new_data->pv_start_pe = data->pv_start_pe;
-    new_data->pvdev = g_strdup (data->pvdev);
-    return new_data;
-}
-
-void bd_lvm_segdata_free (BDLVMSEGdata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data->pvdev);
-    g_free (data);
-}
-
-static BDLVMSEGdata **copy_segs (BDLVMSEGdata **segs) {
-    int len;
-    BDLVMSEGdata **new_segs;
-
-    if (segs == NULL)
-       return NULL;
-
-    for (len = 0; segs[len]; len++)
-        ;
-
-    new_segs = g_new0 (BDLVMSEGdata *, len+1);
-    for (int i = 0; i < len; i++)
-        new_segs[i] = bd_lvm_segdata_copy (segs[i]);
-
-    return new_segs;
-}
-
-static void free_segs (BDLVMSEGdata **segs) {
-    if (segs == NULL)
-       return;
-
-    for (int i = 0; segs[i]; i++)
-        bd_lvm_segdata_free (segs[i]);
-    (g_free) (segs);
-}
-
-BDLVMLVdata* bd_lvm_lvdata_copy (BDLVMLVdata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMLVdata *new_data = g_new0 (BDLVMLVdata, 1);
-
-    new_data->lv_name = g_strdup (data->lv_name);
-    new_data->vg_name = g_strdup (data->vg_name);
-    new_data->uuid = g_strdup (data->uuid);
-    new_data->size = data->size;
-    new_data->attr = g_strdup (data->attr);
-    new_data->segtype = g_strdup (data->segtype);
-    new_data->origin = g_strdup (data->origin);
-    new_data->pool_lv = g_strdup (data->pool_lv);
-    new_data->data_lv = g_strdup (data->data_lv);
-    new_data->metadata_lv = g_strdup (data->metadata_lv);
-    new_data->roles = g_strdup (data->roles);
-    new_data->move_pv = g_strdup (data->move_pv);
-    new_data->data_percent = data->data_percent;
-    new_data->metadata_percent = data->metadata_percent;
-    new_data->copy_percent = data->copy_percent;
-    new_data->lv_tags = g_strdupv (data->lv_tags);
-    new_data->data_lvs = g_strdupv (data->data_lvs);
-    new_data->metadata_lvs = g_strdupv (data->metadata_lvs);
-    new_data->segs = copy_segs (data->segs);
-    return new_data;
-}
-
-void bd_lvm_lvdata_free (BDLVMLVdata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data->lv_name);
-    g_free (data->vg_name);
-    g_free (data->uuid);
-    g_free (data->attr);
-    g_free (data->segtype);
-    g_free (data->origin);
-    g_free (data->pool_lv);
-    g_free (data->data_lv);
-    g_free (data->metadata_lv);
-    g_free (data->roles);
-    g_free (data->move_pv);
-    g_strfreev (data->lv_tags);
-    g_strfreev (data->data_lvs);
-    g_strfreev (data->metadata_lvs);
-    free_segs (data->segs);
-    g_free (data);
-}
-
-BDLVMVDOPooldata* bd_lvm_vdopooldata_copy (BDLVMVDOPooldata *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMVDOPooldata *new_data = g_new0 (BDLVMVDOPooldata, 1);
-
-    new_data->operating_mode = data->operating_mode;
-    new_data->compression_state = data->compression_state;
-    new_data->index_state = data->index_state;
-    new_data->write_policy = data->write_policy;
-    new_data->used_size = data->used_size;
-    new_data->saving_percent = data->saving_percent;
-    new_data->index_memory_size = data->index_memory_size;
-    new_data->deduplication = data->deduplication;
-    new_data->compression = data->compression;
-    return new_data;
-}
-
-void bd_lvm_vdopooldata_free (BDLVMVDOPooldata *data) {
-    if (data == NULL)
-        return;
-
-    g_free (data);
-}
-
-BDLVMCacheStats* bd_lvm_cache_stats_copy (BDLVMCacheStats *data) {
-    if (data == NULL)
-        return NULL;
-
-    BDLVMCacheStats *new = g_new0 (BDLVMCacheStats, 1);
-
-    new->block_size = data->block_size;
-    new->cache_size = data->cache_size;
-    new->cache_used = data->cache_used;
-    new->md_block_size = data->md_block_size;
-    new->md_size = data->md_size;
-    new->md_used = data->md_used;
-    new->read_hits = data->read_hits;
-    new->read_misses = data->read_misses;
-    new->write_hits = data->write_hits;
-    new->write_misses = data->write_misses;
-    new->mode = data->mode;
-
-    return new;
-}
-
-void bd_lvm_cache_stats_free (BDLVMCacheStats *data) {
-    g_free (data);
-}
-
-
-static volatile guint avail_deps = 0;
-static volatile guint avail_features = 0;
-static volatile guint avail_module_deps = 0;
-static GMutex deps_check_lock;
-
-#define DEPS_LVM 0
-#define DEPS_LVM_MASK (1 << DEPS_LVM)
-#define DEPS_LVMDEVICES 1
-#define DEPS_LVMDEVICES_MASK (1 << DEPS_LVMDEVICES)
-#define DEPS_LVMCONFIG 2
-#define DEPS_LVMCONFIG_MASK (1 << DEPS_LVMCONFIG)
-#define DEPS_LAST 3
-
-static const UtilDep deps[DEPS_LAST] = {
-    {"lvm", LVM_MIN_VERSION, "version", "LVM version:\\s+([\\d\\.]+)"},
-    {"lvmdevices", NULL, NULL, NULL},
-    {"lvmconfig", "2.03.17", "--version", "LVM version:\\s+([\\d\\.]+)"},
-};
-
-#define FEATURES_VDO 0
-#define FEATURES_VDO_MASK (1 << FEATURES_VDO)
-#define FEATURES_WRITECACHE 0
-#define FEATURES_WRITECACHE_MASK (1 << FEATURES_WRITECACHE)
-#define FEATURES_LAST 2
-
-static const UtilFeatureDep features[FEATURES_LAST] = {
-    {"lvm", "vdo", "segtypes", NULL},
-    {"lvm", "writecache", "segtypes", NULL},
-};
-
-#define MODULE_DEPS_VDO 0
-#define MODULE_DEPS_VDO_MASK (1 << MODULE_DEPS_VDO)
-#define MODULE_DEPS_LAST 1
-
-static const gchar*const module_deps[MODULE_DEPS_LAST] = { "dm-vdo" };
-
-
-/**
- * bd_lvm_init:
- *
- * Initializes the plugin. **This function is called automatically by the
- * library's initialization functions.**
- *
- */
-gboolean bd_lvm_init (void) {
-    dm_log_with_errno_init ((dm_log_with_errno_fn) redirect_dm_log);
-#ifdef DEBUG
-    dm_log_init_verbose (LOG_DEBUG);
-#else
-    dm_log_init_verbose (LOG_INFO);
-#endif
-
-    return TRUE;
-};
-
-/**
- * bd_lvm_close:
- *
- * Cleans up after the plugin. **This function is called automatically by the
- * library's functions that unload it.**
- *
- */
-void bd_lvm_close (void) {
-    dm_log_with_errno_init (NULL);
-    dm_log_init_verbose (0);
-}
-
-/**
- * bd_lvm_is_tech_avail:
- * @tech: the queried tech
- * @mode: a bit mask of queried modes of operation (#BDLVMTechMode) for @tech
- * @error: (out) (optional): place to store error (details about why the @tech-@mode combination is not available)
- *
- * Returns: whether the @tech-@mode combination is available -- supported by the
- *          plugin implementation and having all the runtime dependencies available
- */
-gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error) {
-    switch (tech) {
-    case BD_LVM_TECH_THIN_CALCS:
-        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
-            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
-                         "Only 'query' supported for thin calculations");
-            return FALSE;
-        } else
-            return TRUE;
-    case BD_LVM_TECH_CALCS:
-        if (mode & ~BD_LVM_TECH_MODE_QUERY) {
-            g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_TECH_UNAVAIL,
-                         "Only 'query' supported for calculations");
-            return FALSE;
-        } else
-            return TRUE;
-    case BD_LVM_TECH_VDO:
-            return check_features (&avail_features, FEATURES_VDO_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
-                   check_module_deps (&avail_module_deps, MODULE_DEPS_VDO_MASK, module_deps, MODULE_DEPS_LAST, &deps_check_lock, error) &&
-                   check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
-    case BD_LVM_TECH_WRITECACHE:
-            return check_features (&avail_features, FEATURES_WRITECACHE_MASK, features, FEATURES_LAST, &deps_check_lock, error) &&
-                   check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
-    case BD_LVM_TECH_DEVICES:
-            return check_deps (&avail_deps, DEPS_LVMDEVICES_MASK, deps, DEPS_LAST, &deps_check_lock, error);
-    case BD_LVM_TECH_CONFIG:
-        return check_deps (&avail_deps, DEPS_LVMCONFIG_MASK, deps, DEPS_LAST, &deps_check_lock, error);
-    default:
-        /* everything is supported by this implementation of the plugin */
-        return check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error);
-    }
-}
-
-static gboolean call_lvm_and_report_error (const gchar **args, const BDExtraArg **extra, gboolean lock_config, GError **error) {
-    gboolean success = FALSE;
-    guint i = 0;
-    guint args_length = g_strv_length ((gchar **) args);
-    g_autofree gchar *config_arg = NULL;
-    g_autofree gchar *devices_arg = NULL;
-
-    if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
-        return FALSE;
-
-    /* don't allow global config string changes during the run */
-    if (lock_config)
-        g_mutex_lock (&global_config_lock);
-
-    /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
-    const gchar **argv = g_new0 (const gchar*, args_length + 4);
-
-    /* construct argv from args with "lvm" prepended */
-    argv[0] = "lvm";
-    for (i=0; i < args_length; i++)
-        argv[i+1] = args[i];
-    if (global_config_str) {
-        config_arg = g_strdup_printf ("--config=%s", global_config_str);
-        argv[++args_length] = config_arg;
-    }
-    if (global_devices_str) {
-        devices_arg = g_strdup_printf ("--devices=%s", global_devices_str);
-        argv[++args_length] = devices_arg;
-    }
-    argv[++args_length] = NULL;
-
-    success = bd_utils_exec_and_report_error (argv, extra, error);
-    if (lock_config)
-        g_mutex_unlock (&global_config_lock);
-    g_free (argv);
-
-    return success;
-}
-
-static gboolean call_lvm_and_capture_output (const gchar **args, const BDExtraArg **extra, gchar **output, GError **error) {
-    gboolean success = FALSE;
-    guint i = 0;
-    guint args_length = g_strv_length ((gchar **) args);
-    g_autofree gchar *config_arg = NULL;
-    g_autofree gchar *devices_arg = NULL;
-
-    if (!check_deps (&avail_deps, DEPS_LVM_MASK, deps, DEPS_LAST, &deps_check_lock, error))
-        return FALSE;
-
-    /* don't allow global config string changes during the run */
-    g_mutex_lock (&global_config_lock);
-
-    /* allocate enough space for the args plus "lvm", "--config", "--devices" and NULL */
-    const gchar **argv = g_new0 (const gchar*, args_length + 4);
-
-    /* construct argv from args with "lvm" prepended */
-    argv[0] = "lvm";
-    for (i=0; i < args_length; i++)
-        argv[i+1] = args[i];
-    if (global_config_str) {
-        config_arg = g_strdup_printf ("--config=%s", global_config_str);
-        argv[++args_length] = config_arg;
-    }
-    if (global_devices_str) {
-        devices_arg = g_strdup_printf ("--devices=%s", global_devices_str);
-        argv[++args_length] = devices_arg;
-    }
-    argv[++args_length] = NULL;
-
-    success = bd_utils_exec_and_capture_output (argv, extra, output, error);
-    g_mutex_unlock (&global_config_lock);
-    g_free (argv);
-
-    return success;
-}
-
-/**
- * parse_lvm_vars:
- * @str: string to parse
- * @num_items: (out): number of parsed items
- *
- * Returns: (transfer full): a GHashTable containing key-value items parsed from the @string
- */
-static GHashTable* parse_lvm_vars (const gchar *str, guint *num_items) {
-    GHashTable *table = NULL;
-    gchar **items = NULL;
-    gchar **item_p = NULL;
-    gchar **key_val = NULL;
-
-    table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-    *num_items = 0;
-
-    items = g_strsplit_set (str, " \t\n", 0);
-    for (item_p=items; *item_p; item_p++) {
-        key_val = g_strsplit (*item_p, "=", 2);
-        if (g_strv_length (key_val) == 2) {
-            /* we only want to process valid lines (with the '=' character) */
-            g_hash_table_insert (table, key_val[0], key_val[1]);
-            g_free (key_val);
-            (*num_items)++;
-        } else
-            /* invalid line, just free key_val */
-            g_strfreev (key_val);
-    }
-
-    g_strfreev (items);
-    return table;
-}
-
-static BDLVMPVdata* get_pv_data_from_table (GHashTable *table, gboolean free_table) {
-    BDLVMPVdata *data = g_new0 (BDLVMPVdata, 1);
-    gchar *value = NULL;
-
-    data->pv_name = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_PV_NAME"));
-    data->pv_uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_PV_UUID"));
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_FREE");
-    if (value)
-        data->pv_free = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->pv_free = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_SIZE");
-    if (value)
-        data->pv_size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->pv_size = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_PE_START");
-    if (value)
-        data->pe_start = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->pe_start = 0;
-
-    data->vg_name = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_VG_NAME"));
-    data->vg_uuid = g_strdup ((gchar*) g_hash_table_lookup (table, "LVM2_VG_UUID"));
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_SIZE");
-    if (value)
-        data->vg_size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->vg_size = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE");
-    if (value)
-        data->vg_free = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->vg_free = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_SIZE");
-    if (value)
-        data->vg_extent_size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->vg_extent_size = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_COUNT");
-    if (value)
-        data->vg_extent_count = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->vg_extent_count = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE_COUNT");
-    if (value)
-        data->vg_free_count = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->vg_free_count = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_COUNT");
-    if (value)
-        data->vg_pv_count = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->vg_pv_count = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_TAGS");
-    if (value)
-        data->pv_tags = g_strsplit (value, ",", -1);
-    else
-        data->pv_tags = NULL;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_MISSING");
-    data->missing = (g_strcmp0 (value, "missing") == 0);
-
-    if (free_table)
-        g_hash_table_destroy (table);
-
-    return data;
-}
-
-static BDLVMVGdata* get_vg_data_from_table (GHashTable *table, gboolean free_table) {
-    BDLVMVGdata *data = g_new0 (BDLVMVGdata, 1);
-    gchar *value = NULL;
-
-    data->name = g_strdup (g_hash_table_lookup (table, "LVM2_VG_NAME"));
-    data->uuid = g_strdup (g_hash_table_lookup (table, "LVM2_VG_UUID"));
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_SIZE");
-    if (value)
-        data->size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->size = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE");
-    if (value)
-        data->free = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->free= 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_SIZE");
-    if (value)
-        data->extent_size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->extent_size = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXTENT_COUNT");
-    if (value)
-        data->extent_count = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->extent_count = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_FREE_COUNT");
-    if (value)
-        data->free_count = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->free_count = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_PV_COUNT");
-    if (value)
-        data->pv_count = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->pv_count = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_EXPORTED");
-    if (value && g_strcmp0 (value, "exported") == 0)
-        data->exported = TRUE;
-    else
-        data->exported = FALSE;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VG_TAGS");
-    if (value)
-        data->vg_tags = g_strsplit (value, ",", -1);
-    else
-        data->vg_tags = NULL;
-
-    if (free_table)
-        g_hash_table_destroy (table);
-
-    return data;
-}
-
-static gchar **prepare_sublvs (gchar **values, gchar *extra_value) {
-  /* LVM2 guarantees: No "/dev/" prefixes or "[unknown]" in a list of sub-lvs. */
-  gboolean found_extra = FALSE;
-  for (int i = 0; values[i]; i++) {
-    gchar *paren = strrchr (values[i], '(');
-    if (paren) {
-      /* LVM2 guarantees: start offsets of sub-lvs are always zero. */
-      *paren = '\0';
-    }
-    if (g_strcmp0 (extra_value, values[i]) == 0)
-      found_extra = TRUE;
-  }
-  if (extra_value && *extra_value && !found_extra) {
-    int len = g_strv_length (values);
-    gchar **new_values = g_new0 (gchar *, len+2);
-    for (int j = 0; j < len; j++)
-      new_values[j] = values[j];
-    new_values[len] = g_strdup (extra_value);
-    g_free (values);
-    values = new_values;
-  }
-  return values;
-}
-
-static BDLVMLVdata* get_lv_data_from_table (GHashTable *table, gboolean free_table) {
-    BDLVMLVdata *data = g_new0 (BDLVMLVdata, 1);
-    gchar *value = NULL;
-
-    data->lv_name = g_strdup (g_hash_table_lookup (table, "LVM2_LV_NAME"));
-    data->vg_name = g_strdup (g_hash_table_lookup (table, "LVM2_VG_NAME"));
-    data->uuid = g_strdup (g_hash_table_lookup (table, "LVM2_LV_UUID"));
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_LV_SIZE");
-    if (value)
-        data->size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->size = 0;
-
-    data->attr = g_strdup (g_hash_table_lookup (table, "LVM2_LV_ATTR"));
-
-    value = g_hash_table_lookup (table, "LVM2_SEGTYPE");
-    if (g_strcmp0 (value, "error") == 0) {
-      /* A segment type "error" appears when "vgreduce
-       * --removemissing" replaces a missing PV with a device mapper
-       * "error" target.  It very likely was a "linear" segment before that
-       * and will again be "linear" after repair.  Let's not expose
-       * this implementation detail.
-       */
-      value = "linear";
-    }
-    data->segtype = g_strdup (value);
-
-    data->origin = g_strdup (g_hash_table_lookup (table, "LVM2_ORIGIN"));
-    data->pool_lv = g_strdup (g_hash_table_lookup (table, "LVM2_POOL_LV"));
-    data->data_lv = g_strdup (g_hash_table_lookup (table, "LVM2_DATA_LV"));
-    data->metadata_lv = g_strdup (g_hash_table_lookup (table, "LVM2_METADATA_LV"));
-    data->roles = g_strdup (g_hash_table_lookup (table, "LVM2_LV_ROLE"));
-
-    data->move_pv = g_strdup (g_hash_table_lookup (table, "LVM2_MOVE_PV"));
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_DATA_PERCENT");
-    if (value)
-        data->data_percent = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->data_percent = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_METADATA_PERCENT");
-    if (value)
-        data->metadata_percent = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->metadata_percent = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_COPY_PERCENT");
-    if (value)
-        data->copy_percent = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->copy_percent = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_LV_TAGS");
-    if (value)
-        data->lv_tags = g_strsplit (value, ",", -1);
-    else
-        data->lv_tags = NULL;
-
-    /* replace '[' and ']' (marking LVs as internal) with spaces and then
-       remove all the leading and trailing whitespace */
-    g_strstrip (g_strdelimit (data->pool_lv, "[]", ' '));
-    g_strstrip (g_strdelimit (data->data_lv, "[]", ' '));
-    g_strstrip (g_strdelimit (data->metadata_lv, "[]", ' '));
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_DEVICES");
-    if (value) {
-      gchar **values = g_strsplit (value, ",", -1);
-
-      /* If values starts with "/dev/", we have a single PV.
-
-         If the list is empty, this is probably an "error" segment
-         resulting from a "vgreduce --removemissing" operation.
-
-         If the value starts with "[unknown]", it is a segment with a
-         missing PV that hasn't been converted to an "error" segment
-         yet.
-
-         Otherwise it is a list of sub-lvs.
-
-         LVM2 guarantees: only one entry if the first is a PV
-         Additional segments are added in merge_lv_data below.
-      */
-      if (!values[0] || g_str_has_prefix (values[0], "[unknown]")) {
-        data->segs = g_new0 (BDLVMSEGdata *, 1);
-        data->segs[0] = NULL;
-        g_strfreev (values);
-      } else if (g_str_has_prefix (values[0], "/dev/")) {
-        data->segs = g_new0 (BDLVMSEGdata *, 2);
-        data->segs[0] = g_new0 (BDLVMSEGdata, 1);
-        data->segs[1] = NULL;
-
-        gchar *paren = strrchr (values[0], '(');
-        if (paren) {
-          data->segs[0]->pv_start_pe = atoi (paren+1);
-          *paren = '\0';
-        }
-        data->segs[0]->pvdev = g_strdup (values[0]);
-        value = (gchar*) g_hash_table_lookup (table, "LVM2_SEG_SIZE_PE");
-        if (value)
-          data->segs[0]->size_pe = g_ascii_strtoull (value, NULL, 0);
-        g_strfreev (values);
-      } else {
-        data->data_lvs = prepare_sublvs (values, data->data_lv);
-        value = (gchar*) g_hash_table_lookup (table, "LVM2_METADATA_DEVICES");
-        data->metadata_lvs = prepare_sublvs (g_strsplit (value ?: "", ",", -1), data->metadata_lv);
-      }
-    }
-
-    if (free_table)
-        g_hash_table_destroy (table);
-
-    return data;
-}
-
-static void merge_lv_data (BDLVMLVdata *data, BDLVMLVdata *more_data) {
-  /* LVM2 guarantees:
-     - more_data->data_lvs is NULL
-     - more_data->metadata_lvs is NULL
-     - more_data->segs has zero or one entry
-     - more_data->seg_type is the same as data->seg_type (after mapping "error" to "linear")
-  */
-
-  if (more_data->segs && more_data->segs[0]) {
-    int i;
-    for (i = 0; data->segs && data->segs[i]; i++)
-      ;
-
-    BDLVMSEGdata **new_segs = g_new0 (BDLVMSEGdata *, i+2);
-    for (i = 0; data->segs && data->segs[i]; i++)
-      new_segs[i] = data->segs[i];
-    new_segs[i] = more_data->segs[0];
-    g_free (data->segs);
-    data->segs = new_segs;
-    more_data->segs[0] = NULL;
-  }
-}
-
-static BDLVMVDOPooldata* get_vdo_data_from_table (GHashTable *table, gboolean free_table) {
-    BDLVMVDOPooldata *data = g_new0 (BDLVMVDOPooldata, 1);
-    gchar *value = NULL;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_OPERATING_MODE");
-    if (g_strcmp0 (value, "recovering") == 0)
-        data->operating_mode = BD_LVM_VDO_MODE_RECOVERING;
-    else if (g_strcmp0 (value, "read-only") == 0)
-        data->operating_mode = BD_LVM_VDO_MODE_READ_ONLY;
-    else if (g_strcmp0 (value, "normal") == 0)
-        data->operating_mode = BD_LVM_VDO_MODE_NORMAL;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO operating mode: %s", value);
-        data->operating_mode = BD_LVM_VDO_MODE_UNKNOWN;
-    }
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_COMPRESSION_STATE");
-    if (g_strcmp0 (value, "online") == 0)
-        data->compression_state = BD_LVM_VDO_COMPRESSION_ONLINE;
-    else if (g_strcmp0 (value, "offline") == 0)
-        data->compression_state = BD_LVM_VDO_COMPRESSION_OFFLINE;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO compression state: %s", value);
-        data->compression_state = BD_LVM_VDO_COMPRESSION_UNKNOWN;
-    }
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_INDEX_STATE");
-    if (g_strcmp0 (value, "error") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_ERROR;
-    else if (g_strcmp0 (value, "closed") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_CLOSED;
-    else if (g_strcmp0 (value, "opening") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_OPENING;
-    else if (g_strcmp0 (value, "closing") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_CLOSING;
-    else if (g_strcmp0 (value, "offline") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_OFFLINE;
-    else if (g_strcmp0 (value, "online") == 0)
-        data->index_state = BD_LVM_VDO_INDEX_ONLINE;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO index state: %s", value);
-        data->index_state = BD_LVM_VDO_INDEX_UNKNOWN;
-    }
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_WRITE_POLICY");
-    if (g_strcmp0 (value, "auto") == 0)
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_AUTO;
-    else if (g_strcmp0 (value, "sync") == 0)
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_SYNC;
-    else if (g_strcmp0 (value, "async") == 0)
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_ASYNC;
-    else {
-        bd_utils_log_format (BD_UTILS_LOG_DEBUG, "Unknown VDO write policy: %s", value);
-        data->write_policy = BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
-    }
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_INDEX_MEMORY_SIZE");
-    if (value)
-        data->index_memory_size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->index_memory_size = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_USED_SIZE");
-    if (value)
-        data->used_size = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->used_size = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_SAVING_PERCENT");
-    if (value)
-        data->saving_percent = g_ascii_strtoull (value, NULL, 0);
-    else
-        data->saving_percent = 0;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_COMPRESSION");
-    if (value && g_strcmp0 (value, "enabled") == 0)
-        data->compression = TRUE;
-    else
-        data->compression = FALSE;
-
-    value = (gchar*) g_hash_table_lookup (table, "LVM2_VDO_DEDUPLICATION");
-    if (value && g_strcmp0 (value, "enabled") == 0)
-        data->deduplication = TRUE;
-    else
-        data->deduplication = FALSE;
-
-    if (free_table)
-        g_hash_table_destroy (table);
-
-    return data;
-}
-
-/**
- * bd_lvm_is_supported_pe_size:
- * @size: size (in bytes) to test
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given size is supported physical extent size or not
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error G_GNUC_UNUSED) {
-    return (((size % 2) == 0) && (size >= (MIN_PE_SIZE)) && (size <= (MAX_PE_SIZE)));
-}
-
-/**
- * bd_lvm_get_supported_pe_sizes:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full) (array fixed-size=25): list of supported PE sizes
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 *bd_lvm_get_supported_pe_sizes (GError **error G_GNUC_UNUSED) {
-    guint8 i;
-    guint64 val = MIN_PE_SIZE;
-    guint8 num_items = ((guint8) round (log2 ((double) MAX_PE_SIZE))) - ((guint8) round (log2 ((double) MIN_PE_SIZE))) + 2;
-    guint64 *ret = g_new0 (guint64, num_items);
-
-    for (i=0; (val <= MAX_PE_SIZE); i++, val = val * 2)
-        ret[i] = val;
-
-    ret[num_items-1] = 0;
-
-    return ret;
-}
-
-/**
- * bd_lvm_get_max_lv_size:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: maximum LV size in bytes
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_max_lv_size (GError **error G_GNUC_UNUSED) {
-    return MAX_LV_SIZE;
-}
-
-/**
- * bd_lvm_round_size_to_pe:
- * @size: size to be rounded
- * @pe_size: physical extent (PE) size or 0 to use the default
- * @roundup: whether to round up or down (ceil or floor)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: @size rounded to @pe_size according to the @roundup
- *
- * Rounds given @size up/down to a multiple of @pe_size according to the value
- * of the @roundup parameter. If the rounded value is too big to fit in the
- * return type, the result is rounded down (floored) regardless of the @roundup
- * parameter.
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error G_GNUC_UNUSED) {
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-    guint64 delta = size % pe_size;
-    if (delta == 0)
-        return size;
-
-    if (roundup && (((G_MAXUINT64 - (pe_size - delta)) >= size)))
-        return size + (pe_size - delta);
-    else
-        return size - delta;
-}
-
-/**
- * bd_lvm_get_lv_physical_size:
- * @lv_size: LV size
- * @pe_size: PE size
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: space taken on disk(s) by the LV with given @size
- *
- * Gives number of bytes needed for an LV with the size @lv_size on an LVM stack
- * using given @pe_size.
- *
- * Tech category: %BD_LVM_TECH_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_lv_physical_size (guint64 lv_size, guint64 pe_size, GError **error) {
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-
-    /* the LV just takes space rounded up to the multiple of extent size */
-    return bd_lvm_round_size_to_pe (lv_size, pe_size, TRUE, error);
-}
-
-/**
- * bd_lvm_get_thpool_padding:
- * @size: size of the thin pool
- * @pe_size: PE size or 0 if the default value should be used
- * @included: if padding is already included in the size
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: size of the padding needed for a thin pool with the given @size
- *         according to the @pe_size and @included
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_thpool_padding (guint64 size, guint64 pe_size, gboolean included, GError **error) {
-    guint64 raw_md_size;
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-
-    if (included)
-        raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_EXISTS);
-    else
-        raw_md_size = (guint64) ceil (size * THPOOL_MD_FACTOR_NEW);
-
-    return MIN (bd_lvm_round_size_to_pe (raw_md_size, pe_size, TRUE, error),
-                bd_lvm_round_size_to_pe (MAX_THPOOL_MD_SIZE, pe_size, TRUE, error));
-}
-
-/**
- * bd_lvm_get_thpool_meta_size:
- * @size: size of the thin pool
- * @chunk_size: chunk size of the thin pool or 0 to use the default
- * @n_snapshots: ignored
- * @error: (out) (optional): place to store error (if any)
- *
- * Note: This function will be changed in 3.0: the @n_snapshots parameter
- *       is currently not used and will be removed.
- *
- * Returns: recommended size of the metadata space for the specified pool
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) {
-    guint64 md_size = 0;
-
-    /* based on lvcreate metadata size calculation */
-    md_size = UINT64_C (64) * size / (chunk_size ? chunk_size : DEFAULT_CHUNK_SIZE);
-
-    if (md_size > MAX_THPOOL_MD_SIZE)
-        md_size = MAX_THPOOL_MD_SIZE;
-    else if (md_size < MIN_THPOOL_MD_SIZE)
-        md_size = MIN_THPOOL_MD_SIZE;
-
-    return md_size;
-}
-
-/**
- * bd_lvm_is_valid_thpool_md_size:
- * @size: the size to be tested
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given size is a valid thin pool metadata size or not
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error G_GNUC_UNUSED) {
-    return ((MIN_THPOOL_MD_SIZE <= size) && (size <= MAX_THPOOL_MD_SIZE));
-}
-
-/**
- * bd_lvm_is_valid_thpool_chunk_size:
- * @size: the size to be tested
- * @discard: whether discard/TRIM is required to be supported or not
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given size is a valid thin pool chunk size or not
- *
- * Tech category: %BD_LVM_TECH_THIN_CALCS no mode (it is ignored)
- */
-gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error G_GNUC_UNUSED) {
-    gdouble size_log2 = 0.0;
-
-    if ((size < MIN_THPOOL_CHUNK_SIZE) || (size > MAX_THPOOL_CHUNK_SIZE))
-        return FALSE;
-
-    /* To support discard, chunk size must be a power of two. Otherwise it must be a
-       multiple of 64 KiB. */
-    if (discard) {
-        size_log2 = log2 ((double) size);
-        return ABS (((int) round (size_log2)) - size_log2) <= INT_FLOAT_EPS;
-    } else
-        return (size % (64 KiB)) == 0;
-}
-
-/**
- * bd_lvm_pvcreate:
- * @device: the device to make PV from
- * @data_alignment: data (first PE) alignment or 0 to use the default
- * @metadata_size: size of the area reserved for metadata or 0 to use the default
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the PV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_pvcreate (const gchar *device, guint64 data_alignment, guint64 metadata_size, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"pvcreate", device, NULL, NULL, NULL};
-    guint next_arg = 2;
-    gchar *dataalign_str = NULL;
-    gchar *metadata_str = NULL;
-    gboolean ret = FALSE;
-
-    if (data_alignment != 0) {
-        dataalign_str = g_strdup_printf ("--dataalignment=%"G_GUINT64_FORMAT"K", data_alignment / 1024);
-        args[next_arg++] = dataalign_str;
-    }
-
-    if (metadata_size != 0) {
-        metadata_str = g_strdup_printf ("--metadatasize=%"G_GUINT64_FORMAT"K", metadata_size / 1024);
-        args[next_arg++] = metadata_str;
-    }
-
-    ret = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free (dataalign_str);
-    g_free (metadata_str);
-
-    return ret;
-}
-
-/**
- * bd_lvm_pvresize:
- * @device: the device to resize
- * @size: the new requested size of the PV or 0 if it should be adjusted to device's size
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the PV's size was successfully changed or not
- *
- * If given @size different from 0, sets the PV's size to the given value (see
- * pvresize(8)). If given @size 0, adjusts the PV's size to the underlying
- * block device's size.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg **extra, GError **error) {
-    gchar *size_str = NULL;
-    const gchar *args[6] = {"pvresize", "-y", NULL, NULL, NULL, NULL};
-    guint8 next_pos = 2;
-    guint8 to_free_pos = 0;
-    gboolean ret = FALSE;
-
-    if (size != 0) {
-        size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
-        args[next_pos] = "--setphysicalvolumesize";
-        next_pos++;
-        args[next_pos] = size_str;
-        to_free_pos = next_pos;
-        next_pos++;
-    }
-
-    args[next_pos] = device;
-
-    ret = call_lvm_and_report_error (args, extra, TRUE, error);
-    if (to_free_pos > 0)
-        g_free ((gchar *) args[to_free_pos]);
-
-    return ret;
-}
-
-/**
- * bd_lvm_pvremove:
- * @device: the PV device to be removed/destroyed
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV removal
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the PV was successfully removed/destroyed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
- */
-gboolean bd_lvm_pvremove (const gchar *device, const BDExtraArg **extra, GError **error) {
-    /* one has to be really persuasive to remove a PV (the double --force is not
-       bug, at least not in this code) */
-    const gchar *args[6] = {"pvremove", "--force", "--force", "--yes", device, NULL};
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-static gboolean extract_pvmove_progress (const gchar *line, guint8 *completion) {
-    gchar *num_start = NULL;
-    gint n_scanned = 0;
-    guint8 try_completion = 0;
-
-    num_start = strrchr (line, ' ');
-    if (!num_start)
-        return FALSE;
-    num_start++;
-    n_scanned = sscanf (num_start, "%hhu", &try_completion);
-    if (n_scanned == 1) {
-        *completion = try_completion;
-        return TRUE;
-    }
-    return FALSE;
-}
-
-/**
- * bd_lvm_pvmove:
- * @src: the PV device to move extents off of
- * @dest: (nullable): the PV device to move extents onto or %NULL
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV move
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the extents from the @src PV where successfully moved or not
- *
- * If @dest is %NULL, VG allocation rules are used for the extents from the @src
- * PV (see pvmove(8)).
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_pvmove (const gchar *src, const gchar *dest, const BDExtraArg **extra, GError **error) {
-    const gchar *args[6] = {"pvmove", "-i", "1", src, NULL, NULL};
-    gint status = 0;
-    if (dest)
-        args[4] = dest;
-
-    return bd_utils_exec_and_report_progress (args, extra, extract_pvmove_progress, &status, error);
-}
-
-/**
- * bd_lvm_pvscan:
- * @device: (nullable): the device to scan for PVs or %NULL
- * @update_cache: whether to update the lvmetad cache or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the PV scan
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the system or @device was successfully scanned for PVs or not
- *
- * The @device argument is used only if @update_cache is %TRUE. Otherwise the
- * whole system is scanned for PVs.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_pvscan (const gchar *device, gboolean update_cache, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"pvscan", NULL, NULL, NULL};
-    if (update_cache) {
-        args[1] = "--cache";
-        args[2] = device;
-    }
-    else
-        if (device)
-            bd_utils_log_format (BD_UTILS_LOG_WARNING, "Ignoring the device argument in pvscan (cache update not requested)");
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-static gboolean _manage_lvm_tags (const gchar *devspec, const gchar **tags, const gchar *action, const gchar *cmd, GError **error) {
-    guint tags_len = g_strv_length ((gchar **) tags);
-    const gchar **argv = g_new0 (const gchar*, 2 * tags_len + 3);
-    guint next_arg = 0;
-    gboolean success = FALSE;
-
-    argv[next_arg++] = cmd;
-    for (guint i = 0; i < tags_len; i++) {
-        argv[next_arg++] = action;
-        argv[next_arg++] = tags[i];
-    }
-    argv[next_arg++] = devspec;
-    argv[next_arg] = NULL;
-
-    success = call_lvm_and_report_error (argv, NULL, TRUE, error);
-    g_free (argv);
-    return success;
-}
-
-/**
- * bd_lvm_add_pv_tags:
- * @device: the device to set PV tags for
- * @tags: (array zero-terminated=1): list of tags to add
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully added to @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_add_pv_tags (const gchar *device, const gchar **tags, GError **error) {
-    return _manage_lvm_tags (device, tags, "--addtag", "pvchange", error);
-}
-
-/**
- * bd_lvm_delete_pv_tags:
- * @device: the device to set PV tags for
- * @tags: (array zero-terminated=1): list of tags to remove
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully removed from @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_delete_pv_tags (const gchar *device, const gchar **tags, GError **error) {
-    return _manage_lvm_tags (device, tags, "--deltag", "pvchange", error);
-}
-
-/**
- * bd_lvm_pvinfo:
- * @device: a PV to get information about or %NULL
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the PV on the given @device or
- * %NULL in case of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMPVdata* bd_lvm_pvinfo (const gchar *device, GError **error) {
-    const gchar *args[10] = {"pvs", "--unit=b", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--noheadings",
-                       "-o", "pv_name,pv_uuid,pv_free,pv_size,pe_start,vg_name,vg_uuid,vg_size," \
-                       "vg_free,vg_extent_size,vg_extent_count,vg_free_count,pv_count,pv_tags,pv_missing",
-                       device, NULL};
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 15)) {
-            g_clear_error (error);
-            g_strfreev (lines);
-            return get_pv_data_from_table (table, TRUE);
-        } else
-            if (table)
-                g_hash_table_destroy (table);
-    }
-    g_strfreev (lines);
-
-    /* getting here means no usable info was found */
-    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                 "Failed to parse information about the PV");
-    return NULL;
-}
-
-/**
- * bd_lvm_pvs:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (array zero-terminated=1): information about PVs found in the system
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMPVdata** bd_lvm_pvs (GError **error) {
-    const gchar *args[9] = {"pvs", "--unit=b", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--noheadings",
-                       "-o", "pv_name,pv_uuid,pv_free,pv_size,pe_start,vg_name,vg_uuid,vg_size," \
-                       "vg_free,vg_extent_size,vg_extent_count,vg_free_count,pv_count,pv_tags,pv_missing",
-                       NULL};
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-    GPtrArray *pvs;
-    BDLVMPVdata *pvdata = NULL;
-
-    pvs = g_ptr_array_new ();
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    if (!success) {
-        if (g_error_matches (*error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
-            /* no output => no VGs, not an error */
-            g_clear_error (error);
-            /* return an empty list */
-            g_ptr_array_add (pvs, NULL);
-            return (BDLVMPVdata **) g_ptr_array_free (pvs, FALSE);
-        }
-        else {
-            /* the error is already populated from the call */
-            g_ptr_array_free (pvs, TRUE);
-            return NULL;
-        }
-    }
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 15)) {
-            /* valid line, try to parse and record it */
-            pvdata = get_pv_data_from_table (table, TRUE);
-            if (pvdata)
-                g_ptr_array_add (pvs, pvdata);
-        } else
-            if (table)
-                g_hash_table_destroy (table);
-    }
-
-    g_strfreev (lines);
-
-    if (pvs->len == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                     "Failed to parse information about PVs");
-        g_ptr_array_free (pvs, TRUE);
-        return NULL;
-    }
-
-    /* returning NULL-terminated array of BDLVMPVdata */
-    g_ptr_array_add (pvs, NULL);
-    return (BDLVMPVdata **) g_ptr_array_free (pvs, FALSE);
-}
-
-/**
- * bd_lvm_vgcreate:
- * @name: name of the newly created VG
- * @pv_list: (array zero-terminated=1): list of PVs the newly created VG should use
- * @pe_size: PE size or 0 if the default value should be used
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG @name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_vgcreate (const gchar *name, const gchar **pv_list, guint64 pe_size, const BDExtraArg **extra, GError **error) {
-    guint i = 0;
-    guint pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
-    const gchar **argv = g_new0 (const gchar*, pv_list_len + 5);
-    pe_size = RESOLVE_PE_SIZE (pe_size);
-    gboolean success = FALSE;
-
-    argv[0] = "vgcreate";
-    argv[1] = "-s";
-    argv[2] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", pe_size / 1024);
-    argv[3] = name;
-    for (i=4; i < (pv_list_len + 4); i++) {
-        argv[i] = pv_list[i-4];
-    }
-    argv[i] = NULL;
-
-    success = call_lvm_and_report_error (argv, extra, TRUE, error);
-    g_free ((gchar *) argv[2]);
-    g_free (argv);
-
-    return success;
-}
-
-/**
- * bd_lvm_vgremove:
- * @vg_name: name of the to be removed VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG removal
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully removed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
- */
-gboolean bd_lvm_vgremove (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"vgremove", "--force", vg_name, NULL};
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgrename:
- * @old_vg_name: old name of the VG to rename
- * @new_vg_name: new name for the @old_vg_name VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG rename
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully renamed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgrename (const gchar *old_vg_name, const gchar *new_vg_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"vgrename", old_vg_name, new_vg_name, NULL};
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgactivate:
- * @vg_name: name of the to be activated VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG activation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully activated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"vgchange", "-ay", vg_name, NULL};
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgdeactivate:
- * @vg_name: name of the to be deactivated VG
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG deactivation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG was successfully deactivated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"vgchange", "-an", vg_name, NULL};
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgextend:
- * @vg_name: name of the to be extended VG
- * @device: PV device to extend the @vg_name VG with
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG extension
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG @vg_name was successfully extended with the given @device or not.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"vgextend", vg_name, device, NULL};
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vgreduce:
- * @vg_name: name of the to be reduced VG
- * @device: (nullable): PV device the @vg_name VG should be reduced of or %NULL
- *                        if the VG should be reduced of the missing PVs
- * @extra: (nullable) (array zero-terminated=1): extra options for the VG reduction
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the VG @vg_name was successfully reduced of the given @device or not
- *
- * Note: This function does not move extents off of the PV before removing
- *       it from the VG. You must do that first by calling #bd_lvm_pvmove.
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"vgreduce", NULL, NULL, NULL, NULL};
-
-    if (!device) {
-        args[1] = "--removemissing";
-        args[2] = "--force";
-        args[3] = vg_name;
-    } else {
-        args[1] = vg_name;
-        args[2] = device;
-    }
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_add_vg_tags:
- * @vg_name: the VG to set tags on
- * @tags: (array zero-terminated=1): list of tags to add
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully added to @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
-    return _manage_lvm_tags (vg_name, tags, "--addtag", "vgchange", error);
-}
-
-/**
- * bd_lvm_delete_vg_tags:
- * @vg_name: the VG to set tags on
- * @tags: (array zero-terminated=1): list of tags to remove
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully removed from @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error) {
-    return _manage_lvm_tags (vg_name, tags, "--deltag", "vgchange", error);
-}
-
-static gboolean _vglock_start_stop (const gchar *vg_name, gboolean start, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"vgchange", NULL, vg_name, NULL};
-
-    if (start)
-        args[1] = "--lockstart";
-    else
-        args[1] = "--lockstop";
-
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-/**
- * bd_lvm_vglock_start:
- * @vg_name: a shared VG to start the lockspace in lvmlockd
- * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
- *                                               (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the lock was successfully started for @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    return _vglock_start_stop (vg_name, TRUE, extra, error);
-}
-
-/**
- * bd_lvm_vglock_stop:
- * @vg_name: a shared VG to stop the lockspace in lvmlockd
- * @extra: (nullable) (array zero-terminated=1): extra options for the vgchange command
- *                                               (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the lock was successfully stopped for @vg_name or not
- *
- * Tech category: %BD_LVM_TECH_SHARED-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error) {
-    return _vglock_start_stop (vg_name, FALSE, extra, error);
-}
-
-/**
- * bd_lvm_vginfo:
- * @vg_name: a VG to get information about
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the @vg_name VG or %NULL in case
- * of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error) {
-    const gchar *args[10] = {"vgs", "--noheadings", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--units=b",
-                       "-o", "name,uuid,size,free,extent_size,extent_count,free_count,pv_count,vg_exported,vg_tags",
-                       vg_name, NULL};
-
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 10)) {
-            g_strfreev (lines);
-            return get_vg_data_from_table (table, TRUE);
-        } else
-            if (table)
-                g_hash_table_destroy (table);
-    }
-    g_strfreev (lines);
-
-    /* getting here means no usable info was found */
-    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                 "Failed to parse information about the VG");
-    return NULL;
-}
-
-/**
- * bd_lvm_vgs:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (array zero-terminated=1): information about VGs found in the system
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVGdata** bd_lvm_vgs (GError **error) {
-    const gchar *args[9] = {"vgs", "--noheadings", "--nosuffix", "--nameprefixes",
-                      "--unquoted", "--units=b",
-                      "-o", "name,uuid,size,free,extent_size,extent_count,free_count,pv_count,vg_tags",
-                      NULL};
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-    GPtrArray *vgs;
-    BDLVMVGdata *vgdata = NULL;
-    GError *l_error = NULL;
-
-    vgs = g_ptr_array_new ();
-
-    success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
-    if (!success) {
-        if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
-            /* no output => no VGs, not an error */
-            g_clear_error (&l_error);
-            /* return an empty list */
-            g_ptr_array_add (vgs, NULL);
-            return (BDLVMVGdata **) g_ptr_array_free (vgs, FALSE);
-        } else {
-            /* the error is already populated from the call */
-            g_ptr_array_free (vgs, TRUE);
-            g_propagate_error (error, l_error);
-            return NULL;
-       }
-    }
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 9)) {
-            /* valid line, try to parse and record it */
-            vgdata = get_vg_data_from_table (table, TRUE);
-            if (vgdata)
-                g_ptr_array_add (vgs, vgdata);
-        } else
-            if (table)
-                g_hash_table_destroy (table);
-    }
-
-    g_strfreev (lines);
-
-    if (vgs->len == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                     "Failed to parse information about VGs");
-        g_ptr_array_free (vgs, TRUE);
-        return NULL;
-    }
-
-    /* returning NULL-terminated array of BDLVMVGdata */
-    g_ptr_array_add (vgs, NULL);
-    return (BDLVMVGdata **) g_ptr_array_free (vgs, FALSE);
-}
-
-/**
- * bd_lvm_lvorigin:
- * @vg_name: name of the VG containing the queried LV
- * @lv_name: name of the queried LV
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): the origin volume for the @vg_name/@lv_name LV or
- * %NULL if failed to determine (@error) is set in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_lvorigin (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    const gchar *args[6] = {"lvs", "--noheadings", "-o", "origin", NULL, NULL};
-    args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    g_free ((gchar *) args[4]);
-
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    return g_strstrip (output);
-}
-
-/**
- * bd_lvm_lvcreate:
- * @vg_name: name of the VG to create a new LV in
- * @lv_name: name of the to-be-created LV
- * @size: requested size of the new LV
- * @type: (nullable): type of the new LV ("striped", "raid1",..., see lvcreate (8))
- * @pv_list: (nullable) (array zero-terminated=1): list of PVs the newly created LV should use or %NULL
- * if not specified
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given @vg_name/@lv_name LV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_lvcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, const gchar *type, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
-    guint8 pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
-    const gchar **args = g_new0 (const gchar*, pv_list_len + 10);
-    gboolean success = FALSE;
-    guint64 i = 0;
-    guint64 j = 0;
-    gchar *size_str = NULL;
-    gchar *type_str = NULL;
-
-    args[i++] = "lvcreate";
-    args[i++] = "-n";
-    args[i++] = lv_name;
-    args[i++] = "-L";
-    size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
-    args[i++] = size_str;
-    args[i++] = "-y";
-    if (type) {
-        if (g_strcmp0 (type, "striped") == 0) {
-            args[i++] = "--stripes";
-            type_str = g_strdup_printf ("%d", pv_list_len);
-            args[i++] = type_str;
-        } else {
-            args[i++] = "--type";
-            args[i++] = type;
-        }
-    }
-    args[i++] = vg_name;
-
-    for (j=0; j < pv_list_len; j++)
-        args[i++] = pv_list[j];
-
-    args[i] = NULL;
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free (size_str);
-    g_free (type_str);
-    g_free (args);
-
-    return success;
-}
-
-/**
- * bd_lvm_lvremove:
- * @vg_name: name of the VG containing the to-be-removed LV
- * @lv_name: name of the to-be-removed LV
- * @force: whether to force removal or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV removal
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully removed or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_REMOVE
- */
-gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean force, const BDExtraArg **extra, GError **error) {
-    /* '--yes' is needed if DISCARD is enabled */
-    const gchar *args[5] = {"lvremove", "--yes", NULL, NULL, NULL};
-    guint8 next_arg = 2;
-    gboolean success = FALSE;
-
-    if (force) {
-        args[next_arg] = "--force";
-        next_arg++;
-    }
-
-    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[next_arg]);
-
-    return success;
-}
-
-/**
- * bd_lvm_lvrename:
- * @vg_name: name of the VG containing the to-be-renamed LV
- * @lv_name: name of the to-be-renamed LV
- * @new_name: new name for the @vg_name/@lv_name LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV rename
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully renamed to
- * @vg_name/@new_name or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"lvrename", vg_name, lv_name, new_name, NULL};
-    return call_lvm_and_report_error (args, extra, TRUE, error);
-}
-
-
-/**
- * bd_lvm_lvresize:
- * @vg_name: name of the VG containing the to-be-resized LV
- * @lv_name: name of the to-be-resized LV
- * @size: the requested new size of the LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully resized or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvresize", "--force", "-L", NULL, NULL, NULL, NULL, NULL};
-    gboolean success = FALSE;
-    guint8 next_arg = 4;
-    g_autofree gchar *lvspec = NULL;
-
-    args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
-
-    /* Starting with 2.03.19 we need to add an extra option to avoid
-       any filesystem related checks by lvresize.
-    */
-    success = bd_utils_check_util_version (deps[DEPS_LVM].name, LVM_VERSION_FSRESIZE,
-                                           deps[DEPS_LVM].ver_arg, deps[DEPS_LVM].ver_regexp, NULL);
-    if (success) {
-      args[next_arg++] = "--fs";
-      args[next_arg++] = "ignore";
-    }
-
-    lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    args[next_arg++] = lvspec;
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[3]);
-
-    return success;
-}
-
-/**
- * bd_lvm_lvrepair:
- * @vg_name: name of the VG containing the to-be-repaired LV
- * @lv_name: name of the to-be-repaired LV
- * @pv_list: (array zero-terminated=1): list of PVs to be used for the repair
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV repair
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully repaired or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvrepair (const gchar *vg_name, const gchar *lv_name, const gchar **pv_list, const BDExtraArg **extra, GError **error) {
-    guint i = 0;
-    guint pv_list_len = pv_list ? g_strv_length ((gchar **) pv_list) : 0;
-    const gchar **argv = g_new0 (const gchar*, pv_list_len + 5);
-    gboolean success = FALSE;
-
-    argv[0] = "lvconvert";
-    argv[1] = "--repair";
-    argv[2] = "--yes";
-    argv[3] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    for (i=4; i < (pv_list_len + 4); i++) {
-        argv[i] = pv_list[i-4];
-    }
-    argv[i] = NULL;
-
-    success = call_lvm_and_report_error (argv, extra, TRUE, error);
-    g_free ((gchar *) argv[3]);
-    g_free (argv);
-
-    return success;
-}
-
-/**
- * bd_lvm_lvactivate:
- * @vg_name: name of the VG containing the to-be-activated LV
- * @lv_name: name of the to-be-activated LV
- * @ignore_skip: whether to ignore the skip flag or not
- * @shared: whether to activate the LV in shared mode (used for shared LVM setups with lvmlockd,
- *          use %FALSE if not sure)
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV activation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully activated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"lvchange", NULL, NULL, NULL, NULL};
-    guint8 next_arg = 2;
-    gboolean success = FALSE;
-
-    if (shared)
-        args[1] = "-asy";
-    else
-        args[1] = "-ay";
-
-    if (ignore_skip) {
-        args[next_arg] = "-K";
-        next_arg++;
-    }
-    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[next_arg]);
-
-    return success;
-}
-
-/**
- * bd_lvm_lvdeactivate:
- * @vg_name: name of the VG containing the to-be-deactivated LV
- * @lv_name: name of the to-be-deactivated LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV deactivation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name LV was successfully deactivated or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"lvchange", "-an", NULL, NULL};
-    gboolean success = FALSE;
-
-    args[2] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[2]);
-
-    return success;
-}
-
-/**
- * bd_lvm_lvsnapshotcreate:
- * @vg_name: name of the VG containing the LV a new snapshot should be created of
- * @origin_name: name of the LV a new snapshot should be created of
- * @snapshot_name: name of the to-be-created snapshot
- * @size: requested size for the snapshot
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name LV
- * was successfully created or not.
- *
- * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvcreate", "-s", "-L", NULL, "-n", snapshot_name, NULL, NULL};
-    gboolean success = FALSE;
-
-    args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
-    args[6] = g_strdup_printf ("%s/%s", vg_name, origin_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[3]);
-    g_free ((gchar *) args[6]);
-
-    return success;
-}
-
-/**
- * bd_lvm_lvsnapshotmerge:
- * @vg_name: name of the VG containing the to-be-merged LV snapshot
- * @snapshot_name: name of the to-be-merged LV snapshot
- * @extra: (nullable) (array zero-terminated=1): extra options for the LV snapshot merge
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@snapshot_name LV snapshot was successfully merged or not
- *
- * Tech category: %BD_LVM_TECH_BASIC_SNAP-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[4] = {"lvconvert", "--merge", NULL, NULL};
-    gboolean success = FALSE;
-
-    args[2] = g_strdup_printf ("%s/%s", vg_name, snapshot_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[2]);
-
-    return success;
-}
-
-/**
- * bd_lvm_add_lv_tags:
- * @vg_name: name of the VG that contains the LV to set tags on
- * @lv_name: name of the LV to set tags on
- * @tags: (array zero-terminated=1): list of tags to add
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully added to @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_add_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
-    g_autofree gchar *lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    return _manage_lvm_tags (lvspec, tags, "--addtag", "lvchange", error);
-}
-
-/**
- * bd_lvm_delete_lv_tags:
- * @vg_name: name of the VG that contains the LV to set tags on
- * @lv_name: name of the LV to set tags on
- * @tags: (array zero-terminated=1): list of tags to remove
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the tags were successfully removed from @device or not
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-gboolean bd_lvm_delete_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error) {
-    g_autofree gchar *lvspec = g_strdup_printf ("%s/%s", vg_name, lv_name);
-    return _manage_lvm_tags (lvspec, tags, "--deltag", "lvchange", error);
-}
-
-/**
- * bd_lvm_lvinfo:
- * @vg_name: name of the VG that contains the LV to get information about
- * @lv_name: name of the LV to get information about
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
- * of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMLVdata* bd_lvm_lvinfo (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--units=b", "-a",
-                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags",
-                       NULL, NULL};
-
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-
-    args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    g_free ((gchar *) args[9]);
-
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 16)) {
-            g_strfreev (lines);
-            return get_lv_data_from_table (table, TRUE);
-        } else
-            if (table)
-                g_hash_table_destroy (table);
-    }
-    g_strfreev (lines);
-
-    /* getting here means no usable info was found */
-    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                 "Failed to parse information about the LV");
-    return NULL;
-}
-
-BDLVMLVdata* bd_lvm_lvinfo_tree (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--units=b", "-a",
-                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags,devices,metadata_devices,seg_size_pe",
-                       NULL, NULL};
-
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-    BDLVMLVdata *result = NULL;
-
-    args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    g_free ((gchar *) args[9]);
-
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 19)) {
-            BDLVMLVdata *lvdata = get_lv_data_from_table (table, TRUE);
-            if (result) {
-                merge_lv_data (result, lvdata);
-                bd_lvm_lvdata_free (lvdata);
-            } else
-                result = lvdata;
-        } else {
-            if (table)
-                g_hash_table_destroy (table);
-        }
-    }
-    g_strfreev (lines);
-
-    if (result == NULL)
-      g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                   "Failed to parse information about the LV");
-    return result;
-}
-
-/**
- * bd_lvm_lvs:
- * @vg_name: (nullable): name of the VG to get information about LVs from
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (array zero-terminated=1): information about LVs found in the given
- * @vg_name VG or in system if @vg_name is %NULL
- *
- * Tech category: %BD_LVM_TECH_BASIC-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMLVdata** bd_lvm_lvs (const gchar *vg_name, GError **error) {
-    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--units=b", "-a",
-                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags",
-                       NULL, NULL};
-
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-    GPtrArray *lvs;
-    BDLVMLVdata *lvdata = NULL;
-    GError *l_error = NULL;
-
-    lvs = g_ptr_array_new ();
-
-    if (vg_name)
-        args[9] = vg_name;
-
-    success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
-    if (!success) {
-        if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
-            /* no output => no LVs, not an error */
-            g_clear_error (&l_error);
-            /* return an empty list */
-            g_ptr_array_add (lvs, NULL);
-            return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
-        }
-        else {
-            /* the error is already populated from the call */
-            g_ptr_array_free (lvs, TRUE);
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-    }
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 16)) {
-            /* valid line, try to parse and record it */
-            lvdata = get_lv_data_from_table (table, TRUE);
-            if (lvdata) {
-                /* ignore duplicate entries in lvs output, these are caused by multi segments LVs */
-                for (gsize i = 0; i < lvs->len; i++) {
-                    if (g_strcmp0 (((BDLVMLVdata *) g_ptr_array_index (lvs, i))->lv_name, lvdata->lv_name) == 0) {
-                        bd_utils_log_format (BD_UTILS_LOG_DEBUG,
-                                             "Duplicate LV entry for '%s' found in lvs output",
-                                             lvdata->lv_name);
-                        bd_lvm_lvdata_free (lvdata);
-                        lvdata = NULL;
-                        break;
-                    }
-                }
-
-                if (lvdata)
-                    g_ptr_array_add (lvs, lvdata);
-            }
-        } else
-            if (table)
-                g_hash_table_destroy (table);
-    }
-
-    g_strfreev (lines);
-
-    if (lvs->len == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                     "Failed to parse information about LVs");
-        g_ptr_array_free (lvs, TRUE);
-        return NULL;
-    }
-
-    /* returning NULL-terminated array of BDLVMLVdata */
-    g_ptr_array_add (lvs, NULL);
-    return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
-}
-
-BDLVMLVdata** bd_lvm_lvs_tree (const gchar *vg_name, GError **error) {
-    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--units=b", "-a",
-                       "-o", "vg_name,lv_name,lv_uuid,lv_size,lv_attr,segtype,origin,pool_lv,data_lv,metadata_lv,role,move_pv,data_percent,metadata_percent,copy_percent,lv_tags,devices,metadata_devices,seg_size_pe",
-                       NULL, NULL};
-
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-    GPtrArray *lvs;
-    BDLVMLVdata *lvdata = NULL;
-    GError *l_error = NULL;
-
-    lvs = g_ptr_array_new ();
-
-    if (vg_name)
-        args[9] = vg_name;
-
-    success = call_lvm_and_capture_output (args, NULL, &output, &l_error);
-    if (!success) {
-        if (g_error_matches (l_error, BD_UTILS_EXEC_ERROR, BD_UTILS_EXEC_ERROR_NOOUT)) {
-            /* no output => no LVs, not an error */
-            g_clear_error (&l_error);
-            /* return an empty list */
-            g_ptr_array_add (lvs, NULL);
-            return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
-        }
-        else {
-            /* the error is already populated from the call */
-            g_ptr_array_free (lvs, TRUE);
-            g_propagate_error (error, l_error);
-            return NULL;
-        }
-    }
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 19)) {
-            /* valid line, try to parse and record it */
-            lvdata = get_lv_data_from_table (table, TRUE);
-            if (lvdata) {
-                for (gsize i = 0; i < lvs->len; i++) {
-                    BDLVMLVdata *other = (BDLVMLVdata *) g_ptr_array_index (lvs, i);
-                    if (g_strcmp0 (other->lv_name, lvdata->lv_name) == 0) {
-                        merge_lv_data (other, lvdata);
-                        bd_lvm_lvdata_free (lvdata);
-                        lvdata = NULL;
-                        break;
-                    }
-                }
-
-                if (lvdata)
-                    g_ptr_array_add (lvs, lvdata);
-            }
-        } else
-            if (table)
-                g_hash_table_destroy (table);
-    }
-
-    g_strfreev (lines);
-
-    if (lvs->len == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                     "Failed to parse information about LVs");
-        g_ptr_array_free (lvs, TRUE);
-        return NULL;
-    }
-
-    /* returning NULL-terminated array of BDLVMLVdata */
-    g_ptr_array_add (lvs, NULL);
-    return (BDLVMLVdata **) g_ptr_array_free (lvs, FALSE);
-}
-
-/**
- * bd_lvm_thpoolcreate:
- * @vg_name: name of the VG to create a thin pool in
- * @lv_name: name of the to-be-created pool LV
- * @size: requested size of the to-be-created pool
- * @md_size: requested metadata size or 0 to use the default
- * @chunk_size: requested chunk size or 0 to use the default
- * @profile: (nullable): profile to use (see lvm(8) for more information) or %NULL to use
- *                         the default
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name thin pool was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thpoolcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, guint64 md_size, guint64 chunk_size, const gchar *profile, const BDExtraArg **extra, GError **error) {
-    const gchar *args[9] = {"lvcreate", "-T", "-L", NULL, NULL, NULL, NULL, NULL, NULL};
-    guint8 next_arg = 4;
-    gboolean success = FALSE;
-
-    args[3] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size/1024);
-
-    if (md_size != 0) {
-        args[next_arg] = g_strdup_printf ("--poolmetadatasize=%"G_GUINT64_FORMAT"K", md_size / 1024);
-        next_arg++;
-    }
-
-    if (chunk_size != 0) {
-        args[next_arg] = g_strdup_printf ("--chunksize=%"G_GUINT64_FORMAT"K", chunk_size / 1024);
-        next_arg++;
-    }
-
-    if (profile) {
-        args[next_arg] = g_strdup_printf ("--profile=%s", profile);
-        next_arg++;
-    }
-
-    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[3]);
-    g_free ((gchar *) args[4]);
-    g_free ((gchar *) args[5]);
-    g_free ((gchar *) args[6]);
-    g_free ((gchar *) args[7]);
-
-    return success;
-}
-
-/**
- * bd_lvm_thlvcreate:
- * @vg_name: name of the VG containing the thin pool providing extents for the to-be-created thin LV
- * @pool_name: name of the pool LV providing extents for the to-be-created thin LV
- * @lv_name: name of the to-be-created thin LV
- * @size: requested virtual size of the to-be-created thin LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name thin LV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thlvcreate (const gchar *vg_name, const gchar *pool_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvcreate", "-T", NULL, "-V", NULL, "-n", lv_name, NULL};
-    gboolean success;
-
-    args[2] = g_strdup_printf ("%s/%s", vg_name, pool_name);
-    args[4] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", size / 1024);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[2]);
-    g_free ((gchar *) args[4]);
-
-    return success;
-}
-
-/**
- * bd_lvm_thlvpoolname:
- * @vg_name: name of the VG containing the queried thin LV
- * @lv_name: name of the queried thin LV
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
- * thin LV or %NULL if failed to determine (@error) is set in those cases)
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_thlvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    const gchar *args[6] = {"lvs", "--noheadings", "-o", "pool_lv", NULL, NULL};
-    args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    g_free ((gchar *) args[4]);
-
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    return g_strstrip (output);
-}
-
-/**
- * bd_lvm_thsnapshotcreate:
- * @vg_name: name of the VG containing the thin LV a new snapshot should be created of
- * @origin_name: name of the thin LV a new snapshot should be created of
- * @snapshot_name: name of the to-be-created snapshot
- * @pool_name: (nullable): name of the thin pool to create the snapshot in or %NULL if not specified
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin LV snapshot creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @snapshot_name snapshot of the @vg_name/@origin_name
- * thin LV was successfully created or not.
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvcreate", "-s", "-n", snapshot_name, NULL, NULL, NULL, NULL};
-    guint next_arg = 4;
-    gboolean success = FALSE;
-
-    if (pool_name) {
-        args[next_arg] = "--thinpool";
-        next_arg++;
-        args[next_arg] = pool_name;
-        next_arg++;
-    }
-
-    args[next_arg] = g_strdup_printf ("%s/%s", vg_name, origin_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[next_arg]);
-
-    return success;
-}
-
-/**
- * bd_lvm_set_global_config:
- * @new_config: (nullable): string representation of the new global libblockdev LVM
- *                          configuration to set or %NULL to reset to default
- * @error: (out) (optional): place to store error (if any)
- *
- *
- * Note: This functions sets configuration options for LVM calls internally
- *       in libblockdev, it doesn't change the global lvm.conf config file.
- *       Calling this function with `backup {backup=0 archive=0}` for example
- *       means `--config=backup {backup=0 archive=0}"` will be added to all
- *       calls libblockdev makes.
- *
- * Returns: whether the new requested global config @new_config was successfully
- *          set or not
- *
- * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
- */
-gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error G_GNUC_UNUSED) {
-    /* XXX: the error attribute will likely be used in the future when
-       some validation comes into the game */
-
-    g_mutex_lock (&global_config_lock);
-
-    /* first free the old value */
-    g_free (global_config_str);
-
-    /* now store the new one */
-    if (!new_config || g_strcmp0 (new_config, "") == 0)
-         global_config_str = NULL;
-    else
-        global_config_str = g_strdup (new_config);
-
-    g_mutex_unlock (&global_config_lock);
-    return TRUE;
-}
-
-/**
- * bd_lvm_get_global_config:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): a copy of a string representation of the currently
- *                           set libblockdev LVM global configuration
- *
- * Note: This function does not change the global `lvm.conf` config
- *       file, see %bd_lvm_set_global_config for details.
- *
- * Tech category: %BD_LVM_TECH_GLOB_CONF no mode (it is ignored)
- */
-gchar* bd_lvm_get_global_config (GError **error G_GNUC_UNUSED) {
-    gchar *ret = NULL;
-
-    g_mutex_lock (&global_config_lock);
-    ret = g_strdup (global_config_str ? global_config_str : "");
-    g_mutex_unlock (&global_config_lock);
-
-    return ret;
-}
-
-/**
- * bd_lvm_set_devices_filter:
- * @devices: (nullable) (array zero-terminated=1): list of devices for lvm commands to work on
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the devices filter was successfully set or not
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error) {
-    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
-        return FALSE;
-
-    g_mutex_lock (&global_config_lock);
-
-    /* first free the old value */
-    g_free (global_devices_str);
-
-    /* now store the new one */
-    if (!devices || !(*devices))
-        global_devices_str = NULL;
-    else
-        global_devices_str = g_strjoinv (",", (gchar **) devices);
-
-    g_mutex_unlock (&global_config_lock);
-    return TRUE;
-}
-
-/**
- * bd_lvm_get_devices_filter:
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full) (array zero-terminated=1): a copy of a string representation of
- *                                                     the currently set LVM devices filter
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gchar** bd_lvm_get_devices_filter (GError **error G_GNUC_UNUSED) {
-    gchar **ret = NULL;
-
-    g_mutex_lock (&global_config_lock);
-
-    if (global_devices_str)
-        ret = g_strsplit (global_devices_str, ",", -1);
-    else
-        ret = NULL;
-
-    g_mutex_unlock (&global_config_lock);
-
-    return ret;
-}
-
-/**
- * bd_lvm_cache_get_default_md_size:
- * @cache_size: size of the cache to determine MD size for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: recommended default size of the cache metadata LV or 0 in case of error
- *
- * Tech category: %BD_LVM_TECH_CACHE_CALCS no mode (it is ignored)
- */
-guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error G_GNUC_UNUSED) {
-    return MAX ((guint64) cache_size / 1000, MIN_CACHE_MD_SIZE);
-}
-
-/**
- * get_lv_type_from_flags: (skip)
- * @meta: getting type for a (future) metadata LV
- *
- * Get LV type string from flags.
- */
-static const gchar* get_lv_type_from_flags (BDLVMCachePoolFlags flags, gboolean meta, GError **error G_GNUC_UNUSED) {
-    if (!meta) {
-        if (flags & BD_LVM_CACHE_POOL_STRIPED)
-            return "striped";
-        else if (flags & BD_LVM_CACHE_POOL_RAID1)
-            return "raid1";
-        else if (flags & BD_LVM_CACHE_POOL_RAID5)
-            return "raid5";
-        else if (flags & BD_LVM_CACHE_POOL_RAID6)
-            return "raid6";
-        else if (flags & BD_LVM_CACHE_POOL_RAID10)
-            return "raid10";
-        else
-            return NULL;
-    } else {
-        if (flags & BD_LVM_CACHE_POOL_META_STRIPED)
-            return "striped";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID1)
-            return "raid1";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID5)
-            return "raid5";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID6)
-            return "raid6";
-        else if (flags & BD_LVM_CACHE_POOL_META_RAID10)
-            return "raid10";
-        else
-            return NULL;
-    }
-}
-
-/**
- * bd_lvm_cache_get_mode_str:
- * @mode: mode to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @mode or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error) {
-    if (mode == BD_LVM_CACHE_MODE_WRITETHROUGH)
-        return "writethrough";
-    else if (mode == BD_LVM_CACHE_MODE_WRITEBACK)
-        return "writeback";
-    else if (mode == BD_LVM_CACHE_MODE_UNKNOWN)
-        return "unknown";
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Invalid mode given: %d", mode);
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_cache_get_mode_from_str:
- * @mode_str: string representation of a cache mode
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: cache mode for the @mode_str or %BD_LVM_CACHE_MODE_UNKNOWN if
- *          failed to determine
- *
- * Tech category: always provided/supported
- */
-BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error) {
-    if (g_strcmp0 (mode_str, "writethrough") == 0)
-        return BD_LVM_CACHE_MODE_WRITETHROUGH;
-    else if (g_strcmp0 (mode_str, "writeback") == 0)
-        return BD_LVM_CACHE_MODE_WRITEBACK;
-    else if (g_strcmp0 (mode_str, "unknown") == 0)
-        return BD_LVM_CACHE_MODE_UNKNOWN;
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Invalid mode given: %s", mode_str);
-        return BD_LVM_CACHE_MODE_UNKNOWN;
-    }
-}
-
-/**
- * bd_lvm_cache_create_pool:
- * @vg_name: name of the VG to create @pool_name in
- * @pool_name: name of the cache pool LV to create
- * @pool_size: desired size of the cache pool @pool_name
- * @md_size: desired size of the @pool_name cache pool's metadata LV or 0 to
- *           use the default
- * @mode: cache mode of the @pool_name cache pool
- * @flags: a combination of (ORed) #BDLVMCachePoolFlags
- * @fast_pvs: (array zero-terminated=1): list of (fast) PVs to create the @pool_name
- *                                       cache pool (and the metadata LV)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cache pool @vg_name/@pool_name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_cache_create_pool (const gchar *vg_name, const gchar *pool_name, guint64 pool_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags, const gchar **fast_pvs, GError **error) {
-    gboolean success = FALSE;
-    const gchar *type = NULL;
-    gchar *name = NULL;
-    gchar *msg = NULL;
-    guint64 progress_id = 0;
-    const gchar *args[10] = {"lvconvert", "-y", "--type", "cache-pool", "--poolmetadata", NULL, "--cachemode", NULL, NULL, NULL};
-    GError *l_error = NULL;
-
-    msg = g_strdup_printf ("Started 'create cache pool %s/%s'", vg_name, pool_name);
-    progress_id = bd_utils_report_started (msg);
-    g_free (msg);
-
-    /* create an LV for the pool */
-    type = get_lv_type_from_flags (flags, FALSE, NULL);
-    success = bd_lvm_lvcreate (vg_name, pool_name, pool_size, type, fast_pvs, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the pool LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 1/3 steps done */
-    bd_utils_report_progress (progress_id, 33, "Created the data LV");
-
-    /* determine the size of the metadata LV */
-    type = get_lv_type_from_flags (flags, TRUE, NULL);
-    if (md_size == 0)
-        md_size = bd_lvm_cache_get_default_md_size (pool_size, &l_error);
-    if (l_error) {
-        g_prefix_error (&l_error, "Failed to determine size for the pool metadata LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-    name = g_strdup_printf ("%s_meta", pool_name);
-
-    /* create the metadata LV */
-    success = bd_lvm_lvcreate (vg_name, name, md_size, type, fast_pvs, NULL, &l_error);
-    if (!success) {
-        g_free (name);
-        g_prefix_error (&l_error, "Failed to create the pool metadata LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 2/3 steps done */
-    bd_utils_report_progress (progress_id, 66, "Created the metadata LV");
-
-    /* create the cache pool from the two LVs */
-    args[5] = name;
-    args[7] = (const gchar *) bd_lvm_cache_get_mode_str (mode, &l_error);
-    if (!args[7]) {
-        g_free ((gchar *) args[5]);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-    name = g_strdup_printf ("%s/%s", vg_name, pool_name);
-    args[8] = name;
-    success = call_lvm_and_report_error (args, NULL, TRUE, &l_error);
-    g_free ((gchar *) args[5]);
-    g_free ((gchar *) args[8]);
-
-    if (!success) {
-        if (l_error)
-            bd_utils_report_finished (progress_id, l_error->message);
-        else
-            bd_utils_report_finished (progress_id, "Completed");
-        g_propagate_error (error, l_error);
-    } else
-        bd_utils_report_finished (progress_id, "Completed");
-
-    /* just return the result of the last step (it sets error on fail) */
-    return success;
-}
-
-/**
- * bd_lvm_cache_attach:
- * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
- * @data_lv: data LV to attach the @cache_pool_lv to
- * @cache_pool_lv: cache pool LV to attach to the @data_lv
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @cache_pool_lv was successfully attached to the @data_lv or not
- *
- * Note: Both @data_lv and @cache_lv will be deactivated before the operation.
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_cache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_pool_lv, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvconvert", "-y", "--type", "cache", "--cachepool", NULL, NULL, NULL};
-    gboolean success = FALSE;
-
-    args[5] = g_strdup_printf ("%s/%s", vg_name, cache_pool_lv);
-    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-
-    g_free ((gchar *) args[5]);
-    g_free ((gchar *) args[6]);
-    return success;
-}
-
-/**
- * bd_lvm_cache_detach:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: name of the cached LV to detach its cache from
- * @destroy: whether to destroy the cache after detach or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cache was successfully detached from the @cached_lv or not
- *
- * Note: synces the cache first
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_cache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
-    /* need to both "assume yes" and "force" to get rid of the interactive
-       questions in case of "--uncache" */
-    const gchar *args[6] = {"lvconvert", "-y", "-f", NULL, NULL, NULL};
-    gboolean success = FALSE;
-
-    args[3] = destroy ? "--uncache" : "--splitcache";
-    args[4] = g_strdup_printf ("%s/%s", vg_name, cached_lv);
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-
-    g_free ((gchar *) args[4]);
-    return success;
-}
-
-/**
- * bd_lvm_cache_create_cached_lv:
- * @vg_name: name of the VG to create a cached LV in
- * @lv_name: name of the cached LV to create
- * @data_size: size of the data LV
- * @cache_size: size of the cache (or cached LV more precisely)
- * @md_size: size of the cache metadata LV or 0 to use the default
- * @mode: cache mode for the cached LV
- * @flags: a combination of (ORed) #BDLVMCachePoolFlags
- * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
- * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cached LV @lv_name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_cache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags,
-                                        const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
-    gboolean success = FALSE;
-    gchar *name = NULL;
-    gchar *msg = NULL;
-    guint64 progress_id = 0;
-    GError *l_error = NULL;
-
-    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
-    progress_id = bd_utils_report_started (msg);
-    g_free (msg);
-
-    name = g_strdup_printf ("%s_cache", lv_name);
-    success = bd_lvm_cache_create_pool (vg_name, name, cache_size, md_size, mode, flags, fast_pvs, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the cache pool '%s': ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 3/5 steps (cache pool creation has 3 steps) done */
-    bd_utils_report_progress (progress_id, 60, "Cache pool created");
-
-    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
-    if (!success) {
-        g_free (name);
-        g_prefix_error (&l_error, "Failed to create the data LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 4/5 steps (cache pool creation has 3 steps) done */
-    bd_utils_report_progress (progress_id, 80, "Data LV created");
-
-    success = bd_lvm_cache_attach (vg_name, lv_name, name, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (error, "Failed to attach the cache pool '%s' to the data LV: ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    bd_utils_report_finished (progress_id, "Completed");
-    g_free (name);
-    return TRUE;
-}
-
-/**
- * bd_lvm_writecache_attach:
- * @vg_name: name of the VG containing the @data_lv and the @cache_pool_lv LVs
- * @data_lv: data LV to attach the @cache_lv to
- * @cache_lv: cache (fast) LV to attach to the @data_lv
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache attachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @cache_lv was successfully attached to the @data_lv or not
- *
- * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_writecache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_lv, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvconvert", "-y", "--type", "writecache", "--cachevol", NULL, NULL, NULL};
-    gboolean success = FALSE;
-
-    /* both LVs need to be inactive for the writecache convert to work */
-    success = bd_lvm_lvdeactivate (vg_name, data_lv, NULL, error);
-    if (!success)
-        return FALSE;
-
-    success = bd_lvm_lvdeactivate (vg_name, cache_lv, NULL, error);
-    if (!success)
-        return FALSE;
-
-    args[5] = g_strdup_printf ("%s/%s", vg_name, cache_lv);
-    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-
-    g_free ((gchar *) args[5]);
-    g_free ((gchar *) args[6]);
-    return success;
-}
-
-/**
- * bd_lvm_writecache_detach:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: name of the cached LV to detach its cache from
- * @destroy: whether to destroy the cache after detach or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the cache detachment
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cache was successfully detached from the @cached_lv or not
- *
- * Note: synces the cache first
- *
- * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_writecache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error) {
-    return bd_lvm_cache_detach (vg_name, cached_lv, destroy, extra, error);
-}
-
-/**
- * bd_lvm_writecache_create_cached_lv:
- * @vg_name: name of the VG to create a cached LV in
- * @lv_name: name of the cached LV to create
- * @data_size: size of the data LV
- * @cache_size: size of the cache (or cached LV more precisely)
- * @slow_pvs: (array zero-terminated=1): list of slow PVs (used for the data LV)
- * @fast_pvs: (array zero-terminated=1): list of fast PVs (used for the cache LV)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the cached LV @lv_name was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_WRITECACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_writecache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size,
-                                             const gchar **slow_pvs, const gchar **fast_pvs, GError **error) {
-    gboolean success = FALSE;
-    gchar *name = NULL;
-    gchar *msg = NULL;
-    guint64 progress_id = 0;
-    GError *l_error = NULL;
-
-    msg = g_strdup_printf ("Started 'create cached LV %s/%s'", vg_name, lv_name);
-    progress_id = bd_utils_report_started (msg);
-    g_free (msg);
-
-    name = g_strdup_printf ("%s_writecache", lv_name);
-    success = bd_lvm_lvcreate (vg_name, name, cache_size, NULL, fast_pvs, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to create the cache LV '%s': ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 1/3 steps done */
-    bd_utils_report_progress (progress_id, 33, "Cache LV created");
-
-    success = bd_lvm_lvcreate (vg_name, lv_name, data_size, NULL, slow_pvs, NULL, &l_error);
-    if (!success) {
-        g_free (name);
-        g_prefix_error (&l_error, "Failed to create the data LV: ");
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    /* 2/3 steps done */
-    bd_utils_report_progress (progress_id, 66, "Data LV created");
-
-    success = bd_lvm_writecache_attach (vg_name, lv_name, name, NULL, &l_error);
-    if (!success) {
-        g_prefix_error (&l_error, "Failed to attach the cache LV '%s' to the data LV: ", name);
-        g_free (name);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
-
-    bd_utils_report_finished (progress_id, "Completed");
-    g_free (name);
-    return TRUE;
-}
-
-/**
- * bd_lvm_cache_pool_name:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: cached LV to get the name of the its pool LV for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: name of the cache pool LV used by the @cached_lv or %NULL in case of error
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_cache_pool_name (const gchar *vg_name, const gchar *cached_lv, GError **error) {
-    gchar *ret = NULL;
-    gchar *name_start = NULL;
-    gchar *name_end = NULL;
-    gchar *pool_name = NULL;
-
-    /* same as for a thin LV, but with square brackets */
-    ret = bd_lvm_thlvpoolname (vg_name, cached_lv, error);
-    if (!ret)
-        return NULL;
-
-    name_start = strchr (ret, '[');
-    if (!name_start) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Failed to determine cache pool name from: '%s'", ret);
-        g_free (ret);
-        return NULL;
-    }
-    name_start++;
-
-    name_end = strchr (ret, ']');
-    if (!name_end) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Failed to determine cache pool name from: '%s'", ret);
-        g_free (ret);
-        return NULL;
-    }
-
-    pool_name = g_strndup (name_start, name_end - name_start);
-    g_free (ret);
-
-    return pool_name;
-}
-
-/**
- * bd_lvm_cache_stats:
- * @vg_name: name of the VG containing the @cached_lv
- * @cached_lv: cached LV to get stats for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: stats for the @cached_lv or %NULL in case of error
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error) {
-    struct dm_pool *pool = NULL;
-    struct dm_task *task = NULL;
-    struct dm_info info;
-    struct dm_status_cache *status = NULL;
-    gchar *map_name = NULL;
-    guint64 start = 0;
-    guint64 length = 0;
-    gchar *type = NULL;
-    gchar *params = NULL;
-    BDLVMCacheStats *ret = NULL;
-    BDLVMLVdata *lvdata = NULL;
-
-    if (geteuid () != 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_ROOT,
-                     "Not running as root, cannot query DM maps");
-        return NULL;
-    }
-
-    lvdata = bd_lvm_lvinfo (vg_name, cached_lv, error);
-    if (!lvdata)
-        return NULL;
-
-    pool = dm_pool_create ("bd-pool", 20);
-
-    if (g_strcmp0 (lvdata->segtype, "thin-pool") == 0)
-        map_name = dm_build_dm_name (pool, vg_name, lvdata->data_lv, NULL);
-    else
-        /* translate the VG+LV name into the DM map name */
-        map_name = dm_build_dm_name (pool, vg_name, cached_lv, NULL);
-
-    bd_lvm_lvdata_free (lvdata);
-
-    task = dm_task_create (DM_DEVICE_STATUS);
-    if (!task) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to create DM task for the cache map '%s': ", map_name);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (dm_task_set_name (task, map_name) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to create DM task for the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (dm_task_run (task) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to run the DM task for the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (dm_task_get_info (task, &info) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to get task info for the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    if (!info.exists) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_NOCACHE,
-                     "The cache map '%s' doesn't exist: ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    dm_get_next_target (task, NULL, &start, &length, &type, &params);
-
-    if (dm_get_status_cache (pool, params, &status) == 0) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                     "Failed to get status of the cache map '%s': ", map_name);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        return NULL;
-    }
-
-    ret = g_new0 (BDLVMCacheStats, 1);
-    ret->block_size = status->block_size * SECTOR_SIZE;
-    ret->cache_size = status->total_blocks * ret->block_size;
-    ret->cache_used = status->used_blocks * ret->block_size;
-
-    ret->md_block_size = status->metadata_block_size * SECTOR_SIZE;
-    ret->md_size = status->metadata_total_blocks * ret->md_block_size;
-    ret->md_used = status->metadata_used_blocks * ret->md_block_size;
-
-    ret->read_hits = status->read_hits;
-    ret->read_misses = status->read_misses;
-    ret->write_hits = status->write_hits;
-    ret->write_misses = status->write_misses;
-
-    if (status->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH)
-        ret->mode = BD_LVM_CACHE_MODE_WRITETHROUGH;
-    else if (status->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
-        ret->mode = BD_LVM_CACHE_MODE_WRITEBACK;
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_CACHE_INVAL,
-                      "Failed to determine status of the cache from '%"G_GUINT64_FORMAT"': ",
-                      status->feature_flags);
-        dm_task_destroy (task);
-        dm_pool_destroy (pool);
-        bd_lvm_cache_stats_free (ret);
-        return NULL;
-    }
-
-    dm_task_destroy (task);
-    dm_pool_destroy (pool);
-
-    return ret;
-}
-
-/**
- * bd_lvm_thpool_convert:
- * @vg_name: name of the VG to create the new thin pool in
- * @data_lv: name of the LV that should become the data part of the new pool
- * @metadata_lv: name of the LV that should become the metadata part of the new pool
- * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Converts the @data_lv and @metadata_lv into a new thin pool in the @vg_name
- * VG.
- *
- * Returns: whether the new thin pool was successfully created from @data_lv and
- *          @metadata_lv or not
- *
- * Tech category: %BD_LVM_TECH_THIN-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_thpool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvconvert", "--yes", "--type", "thin-pool", "--poolmetadata", metadata_lv, NULL, NULL};
-    gboolean success = FALSE;
-
-    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[6]);
-
-    if (success && name)
-        success = bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
-
-    return success;
-}
-
-/**
- * bd_lvm_cache_pool_convert:
- * @vg_name: name of the VG to create the new thin pool in
- * @data_lv: name of the LV that should become the data part of the new pool
- * @metadata_lv: name of the LV that should become the metadata part of the new pool
- * @name: (nullable): name for the thin pool (if %NULL, the name @data_lv is inherited)
- * @extra: (nullable) (array zero-terminated=1): extra options for the thin pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Converts the @data_lv and @metadata_lv into a new cache pool in the @vg_name
- * VG.
- *
- * Returns: whether the new cache pool was successfully created from @data_lv and
- *          @metadata_lv or not
- *
- * Tech category: %BD_LVM_TECH_CACHE-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_cache_pool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error) {
-    const gchar *args[8] = {"lvconvert", "--yes", "--type", "cache-pool", "--poolmetadata", metadata_lv, NULL, NULL};
-    gboolean success = FALSE;
-
-    args[6] = g_strdup_printf ("%s/%s", vg_name, data_lv);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[6]);
-
-    if (success && name)
-        success = bd_lvm_lvrename (vg_name, data_lv, name, NULL, error);
-
-    return success;
-}
-
-/**
- * bd_lvm_vdo_pool_create:
- * @vg_name: name of the VG to create a new LV in
- * @lv_name: name of the to-be-created VDO LV
- * @pool_name: (nullable): name of the to-be-created VDO pool LV or %NULL for default name
- * @data_size: requested size of the data VDO LV (physical size of the @pool_name VDO pool LV)
- * @virtual_size: requested virtual_size of the @lv_name VDO LV
- * @index_memory: amount of index memory (in bytes) or 0 for default
- * @compression: whether to enable compression or not
- * @deduplication: whether to enable deduplication or not
- * @write_policy: write policy for the volume
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the given @vg_name/@lv_name VDO LV was successfully created or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE
- */
-gboolean bd_lvm_vdo_pool_create (const gchar *vg_name, const gchar *lv_name, const gchar *pool_name, guint64 data_size, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
-    const gchar *args[16] = {"lvcreate", "--type", "vdo", "-n", lv_name, "-L", NULL, "-V", NULL,
-                             "--compression", compression ? "y" : "n",
-                             "--deduplication", deduplication ? "y" : "n",
-                             "-y", NULL, NULL};
-    gboolean success = FALSE;
-    gchar *old_config = NULL;
-    const gchar *write_policy_str = NULL;
-
-    write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
-    if (!write_policy_str)
-        return FALSE;
-
-    args[6] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", data_size / 1024);
-    args[8] = g_strdup_printf ("%"G_GUINT64_FORMAT"K", virtual_size / 1024);
-
-    if (pool_name) {
-        args[14] = g_strdup_printf ("%s/%s", vg_name, pool_name);
-    } else
-        args[14] = vg_name;
-
-    /* index_memory and write_policy can be specified only using the config */
-    g_mutex_lock (&global_config_lock);
-    old_config = global_config_str;
-    if (index_memory != 0)
-        global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
-                                                                                                                                     index_memory / (1024 * 1024),
-                                                                                                                                     write_policy_str);
-    else
-        global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
-                                                                                        write_policy_str);
-
-    success = call_lvm_and_report_error (args, extra, FALSE, error);
-
-    g_free (global_config_str);
-    global_config_str = old_config;
-    g_mutex_unlock (&global_config_lock);
-
-    g_free ((gchar *) args[6]);
-    g_free ((gchar *) args[8]);
-
-    if (pool_name)
-        g_free ((gchar *) args[14]);
-
-    return success;
-}
-
-static gboolean _vdo_set_compression_deduplication (const gchar *vg_name, const gchar *pool_name, const gchar *op, gboolean enable, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"lvchange", op, enable ? "y" : "n", NULL, NULL};
-    gboolean success = FALSE;
-
-    args[3] = g_strdup_printf ("%s/%s", vg_name, pool_name);
-
-    success = call_lvm_and_report_error (args, extra, TRUE, error);
-    g_free ((gchar *) args[3]);
-
-    return success;
-}
-
-/**
- * bd_lvm_vdo_enable_compression:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to enable compression on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether compression was successfully enabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_enable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return _vdo_set_compression_deduplication (vg_name, pool_name, "--compression", TRUE, extra, error);
-}
-
-/**
- * bd_lvm_vdo_disable_compression:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to disable compression on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether compression was successfully disabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_disable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return _vdo_set_compression_deduplication (vg_name, pool_name, "--compression", FALSE, extra, error);
-}
-
-/**
- * bd_lvm_vdo_enable_deduplication:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to enable deduplication on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether deduplication was successfully enabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_enable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return _vdo_set_compression_deduplication (vg_name, pool_name, "--deduplication", TRUE, extra, error);
-}
-
-/**
- * bd_lvm_vdo_enable_deduplication:
- * @vg_name: name of the VG containing the to-be-changed VDO pool LV
- * @pool_name: name of the VDO pool LV to disable deduplication on
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO change
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether deduplication was successfully disabled on @vg_name/@pool_name LV or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_disable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error) {
-    return _vdo_set_compression_deduplication (vg_name, pool_name, "--deduplication", FALSE, extra, error);
-}
-
-/**
- * bd_lvm_vdo_info:
- * @vg_name: name of the VG that contains the LV to get information about
- * @lv_name: name of the LV to get information about
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): information about the @vg_name/@lv_name LV or %NULL in case
- * of error (the @error) gets populated in those cases)
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVDOPooldata* bd_lvm_vdo_info (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    const gchar *args[11] = {"lvs", "--noheadings", "--nosuffix", "--nameprefixes",
-                       "--unquoted", "--units=b", "-a",
-                       "-o", "vdo_operating_mode,vdo_compression_state,vdo_index_state,vdo_write_policy,vdo_index_memory_size,vdo_used_size,vdo_saving_percent,vdo_compression,vdo_deduplication",
-                       NULL, NULL};
-
-    GHashTable *table = NULL;
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    gchar **lines = NULL;
-    gchar **lines_p = NULL;
-    guint num_items;
-
-    args[9] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    g_free ((gchar *) args[9]);
-
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    lines = g_strsplit (output, "\n", 0);
-    g_free (output);
-
-    for (lines_p = lines; *lines_p; lines_p++) {
-        table = parse_lvm_vars ((*lines_p), &num_items);
-        if (table && (num_items == 9)) {
-            g_strfreev (lines);
-            return get_vdo_data_from_table (table, TRUE);
-        } else if (table)
-            g_hash_table_destroy (table);
-    }
-    g_strfreev (lines);
-
-    /* getting here means no usable info was found */
-    g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_PARSE,
-                 "Failed to parse information about the VDO LV");
-    return NULL;
-}
-
-/**
- * bd_lvm_vdo_resize:
- * @vg_name: name of the VG containing the to-be-resized VDO LV
- * @lv_name: name of the to-be-resized VDO LV
- * @size: the requested new size of the VDO LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO LV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@lv_name VDO LV was successfully resized or not
- *
- * Note: Reduction needs to process TRIM for reduced disk area to unmap used data blocks
- *       from the VDO pool LV and it may take a long time.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_resize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    return bd_lvm_lvresize (vg_name, lv_name, size, extra, error);
-}
-
-/**
- * bd_lvm_vdo_pool_resize:
- * @vg_name: name of the VG containing the to-be-resized VDO pool LV
- * @pool_name: name of the to-be-resized VDO pool LV
- * @size: the requested new size of the VDO pool LV
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool LV resize
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @vg_name/@pool_name VDO pool LV was successfully resized or not
- *
- * Note: Size of the VDO pool LV can be only extended, not reduced.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_pool_resize (const gchar *vg_name, const gchar *pool_name, guint64 size, const BDExtraArg **extra, GError **error) {
-    BDLVMLVdata *info = NULL;
-
-    info = bd_lvm_lvinfo (vg_name, pool_name, error);
-    if (!info)
-        return FALSE;
-
-    if (info->size >= size) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_NOT_SUPPORTED,
-                     "Reducing physical size of the VDO pool LV is not supported.");
-        bd_lvm_lvdata_free (info);
-        return FALSE;
-    }
-
-    bd_lvm_lvdata_free (info);
-
-    return bd_lvm_lvresize (vg_name, pool_name, size, extra, error);
-}
-
-/**
- * bd_lvm_vdo_pool_convert:
- * @vg_name: name of the VG that contains @pool_lv
- * @pool_lv: name of the LV that should become the new VDO pool LV
- * @name: (nullable): name for the VDO LV or %NULL for default name
- * @virtual_size: virtual size for the new VDO LV
- * @index_memory: amount of index memory (in bytes) or 0 for default
- * @compression: whether to enable compression or not
- * @deduplication: whether to enable deduplication or not
- * @write_policy: write policy for the volume
- * @extra: (nullable) (array zero-terminated=1): extra options for the VDO pool creation
- *                                                 (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Converts the @pool_lv into a new VDO pool LV in the @vg_name VG and creates a new
- * @name VDO LV with size @virtual_size.
- *
- * Note: All data on @pool_lv will be irreversibly destroyed.
- *
- * Returns: whether the new VDO pool LV was successfully created from @pool_lv and or not
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_CREATE&%BD_LVM_TECH_MODE_MODIFY
- */
-gboolean bd_lvm_vdo_pool_convert (const gchar *vg_name, const gchar *pool_lv, const gchar *name, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error) {
-    const gchar *args[14] = {"lvconvert", "--yes", "--type", "vdo-pool",
-                             "--compression", compression ? "y" : "n",
-                             "--deduplication", deduplication ? "y" : "n",
-                             NULL, NULL, NULL, NULL, NULL, NULL};
-    gboolean success = FALSE;
-    guint next_arg = 4;
-    gchar *size_str = NULL;
-    gchar *lv_spec = NULL;
-    gchar *old_config = NULL;
-    const gchar *write_policy_str = NULL;
-
-    write_policy_str = bd_lvm_get_vdo_write_policy_str (write_policy, error);
-    if (!write_policy_str)
-        return FALSE;
-
-    if (name) {
-        args[next_arg++] = "-n";
-        args[next_arg++] = name;
-    }
-
-    args[next_arg++] = "-V";
-    size_str = g_strdup_printf ("%"G_GUINT64_FORMAT"K", virtual_size / 1024);
-    args[next_arg++] = size_str;
-    lv_spec = g_strdup_printf ("%s/%s", vg_name, pool_lv);
-    args[next_arg++] = lv_spec;
-
-    /* index_memory and write_policy can be specified only using the config */
-    g_mutex_lock (&global_config_lock);
-    old_config = global_config_str;
-    if (index_memory != 0)
-        global_config_str = g_strdup_printf ("%s allocation {vdo_index_memory_size_mb=%"G_GUINT64_FORMAT" vdo_write_policy=\"%s\"}", old_config ? old_config : "",
-                                                                                                                                     index_memory / (1024 * 1024),
-                                                                                                                                     write_policy_str);
-    else
-        global_config_str = g_strdup_printf ("%s allocation {vdo_write_policy=\"%s\"}", old_config ? old_config : "",
-                                                                                        write_policy_str);
-
-    success = call_lvm_and_report_error (args, extra, FALSE, error);
-
-    g_free (global_config_str);
-    global_config_str = old_config;
-    g_mutex_unlock (&global_config_lock);
-
-    g_free (size_str);
-    g_free (lv_spec);
-
-    return success;
-}
-
-/**
- * bd_lvm_vdolvpoolname:
- * @vg_name: name of the VG containing the queried VDO LV
- * @lv_name: name of the queried VDO LV
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): the name of the pool volume for the @vg_name/@lv_name
- * VDO LV or %NULL if failed to determine (@error) is set in those cases)
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-gchar* bd_lvm_vdolvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error) {
-    gboolean success = FALSE;
-    gchar *output = NULL;
-    const gchar *args[6] = {"lvs", "--noheadings", "-o", "pool_lv", NULL, NULL};
-    args[4] = g_strdup_printf ("%s/%s", vg_name, lv_name);
-
-    success = call_lvm_and_capture_output (args, NULL, &output, error);
-    g_free ((gchar *) args[4]);
-
-    if (!success)
-        /* the error is already populated from the call */
-        return NULL;
-
-    return g_strstrip (output);
-}
-
-/**
- * bd_lvm_get_vdo_operating_mode_str:
- * @mode: mode to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @mode or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_operating_mode_str (BDLVMVDOOperatingMode mode, GError **error) {
-    switch (mode) {
-    case BD_LVM_VDO_MODE_RECOVERING:
-        return "recovering";
-    case BD_LVM_VDO_MODE_READ_ONLY:
-        return "read-only";
-    case BD_LVM_VDO_MODE_NORMAL:
-        return "normal";
-    case BD_LVM_VDO_MODE_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO operating mode.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_compression_state_str:
- * @state: state to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @state or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_compression_state_str (BDLVMVDOCompressionState state, GError **error) {
-    switch (state) {
-    case BD_LVM_VDO_COMPRESSION_ONLINE:
-        return "online";
-    case BD_LVM_VDO_COMPRESSION_OFFLINE:
-        return "offline";
-    case BD_LVM_VDO_COMPRESSION_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO compression state.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_index_state_str:
- * @state: state to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @state or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_index_state_str (BDLVMVDOIndexState state, GError **error) {
-    switch (state) {
-    case BD_LVM_VDO_INDEX_ERROR:
-        return "error";
-    case BD_LVM_VDO_INDEX_CLOSED:
-        return "closed";
-    case BD_LVM_VDO_INDEX_OPENING:
-        return "opening";
-    case BD_LVM_VDO_INDEX_CLOSING:
-        return "closing";
-    case BD_LVM_VDO_INDEX_OFFLINE:
-        return "offline";
-    case BD_LVM_VDO_INDEX_ONLINE:
-        return "online";
-    case BD_LVM_VDO_INDEX_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO index state.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_write_policy_str:
- * @policy: policy to get the string representation for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: string representation of @policy or %NULL in case of error
- *
- * Tech category: always provided/supported
- */
-const gchar* bd_lvm_get_vdo_write_policy_str (BDLVMVDOWritePolicy policy, GError **error) {
-    switch (policy) {
-    case BD_LVM_VDO_WRITE_POLICY_AUTO:
-        return "auto";
-    case BD_LVM_VDO_WRITE_POLICY_SYNC:
-        return "sync";
-    case BD_LVM_VDO_WRITE_POLICY_ASYNC:
-        return "async";
-    case BD_LVM_VDO_WRITE_POLICY_UNKNOWN:
-        return "unknown";
-    default:
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Invalid LVM VDO write policy.");
-        return NULL;
-    }
-}
-
-/**
- * bd_lvm_get_vdo_write_policy_from_str:
- * @policy_str: string representation of a policy
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: write policy for the @policy_str or %BD_LVM_VDO_WRITE_POLICY_UNKNOWN if
- *          failed to determine
- *
- * Tech category: always provided/supported
- */
-BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_str, GError **error) {
-    if (g_strcmp0 (policy_str, "auto") == 0)
-        return BD_LVM_VDO_WRITE_POLICY_AUTO;
-    else if (g_strcmp0 (policy_str, "sync") == 0)
-        return BD_LVM_VDO_WRITE_POLICY_SYNC;
-    else if (g_strcmp0 (policy_str, "async") == 0)
-        return BD_LVM_VDO_WRITE_POLICY_ASYNC;
-    else {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_VDO_POLICY_INVAL,
-                     "Invalid policy given: %s", policy_str);
-        return BD_LVM_VDO_WRITE_POLICY_UNKNOWN;
-    }
-}
-
-/**
- * bd_lvm_vdo_get_stats_full:
- * @vg_name: name of the VG that contains @pool_name VDO pool
- * @pool_name: name of the VDO pool to get statistics for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full) (element-type utf8 utf8): hashtable of type string - string of available
- *                                                    statistics or %NULL in case of error
- *                                                    (@error gets populated in those cases)
- *
- * Statistics are collected from the values exposed by the kernel `dm-vdo` module.
- *
- * Some of the keys are computed to mimic the information produced by the vdo tools.
- * Please note the contents of the hashtable may vary depending on the actual dm-vdo module version.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error) {
-    g_autofree gchar *kvdo_name = g_strdup_printf ("%s-%s-%s", vg_name, pool_name, VDO_POOL_SUFFIX);
-    return vdo_get_stats_full (kvdo_name, error);
-}
-
-/**
- * bd_lvm_vdo_get_stats:
- * @vg_name: name of the VG that contains @pool_name VDO pool
- * @pool_name: name of the VDO pool to get statistics for
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): a structure containing selected statistics or %NULL in case of error
- *                           (@error gets populated in those cases)
- *
- * In contrast to @bd_lvm_vdo_get_stats_full this function will only return selected statistics
- * in a fixed structure. In case a value is not available, -1 would be returned.
- *
- * Tech category: %BD_LVM_TECH_VDO-%BD_LVM_TECH_MODE_QUERY
- */
-BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error) {
-    GHashTable *full_stats = NULL;
-    BDLVMVDOStats *stats = NULL;
-
-    full_stats = bd_lvm_vdo_get_stats_full (vg_name, pool_name, error);
-    if (!full_stats)
-        return NULL;
-
-    stats = g_new0 (BDLVMVDOStats, 1);
-    get_stat_val64_default (full_stats, "blockSize", &stats->block_size, -1);
-    get_stat_val64_default (full_stats, "logicalBlockSize", &stats->logical_block_size, -1);
-    get_stat_val64_default (full_stats, "physicalBlocks", &stats->physical_blocks, -1);
-    get_stat_val64_default (full_stats, "dataBlocksUsed", &stats->data_blocks_used, -1);
-    get_stat_val64_default (full_stats, "overheadBlocksUsed", &stats->overhead_blocks_used, -1);
-    get_stat_val64_default (full_stats, "logicalBlocksUsed", &stats->logical_blocks_used, -1);
-    get_stat_val64_default (full_stats, "usedPercent", &stats->used_percent, -1);
-    get_stat_val64_default (full_stats, "savingPercent", &stats->saving_percent, -1);
-    if (!get_stat_val_double (full_stats, "writeAmplificationRatio", &stats->write_amplification_ratio))
-        stats->write_amplification_ratio = -1;
-
-    g_hash_table_destroy (full_stats);
-
-    return stats;
-}
-
-/* check whether the LVM devices file is enabled by LVM
- * we use the existence of the "lvmdevices" command to check whether the feature is available
- * or not, but this can still be disabled either in LVM or in lvm.conf
- */
-static gboolean _lvm_devices_enabled () {
-    const gchar *args[5] = {"config", "--typeconfig", NULL, "devices/use_devicesfile", NULL};
-    gboolean ret = FALSE;
-    GError *loc_error = NULL;
-    gchar *output = NULL;
-    gboolean enabled = FALSE;
-    gint scanned = 0;
-
-    /* try full config first -- if we get something from this it means the feature is
-       explicitly enabled or disabled by system lvm.conf or using the --config option */
-    args[2] = "full";
-    ret = call_lvm_and_capture_output (args, NULL, &output, &loc_error);
-    if (ret) {
-        scanned = sscanf (output, "use_devicesfile=%u", &enabled);
-        g_free (output);
-        if (scanned != 1)
-            return FALSE;
-
-        return enabled;
-    } else {
-        g_clear_error (&loc_error);
-        g_free (output);
-    }
-
-    output = NULL;
-
-    /* now try default */
-    args[2] = "default";
-    ret = call_lvm_and_capture_output (args, NULL, &output, &loc_error);
-    if (ret) {
-        scanned = sscanf (output, "# use_devicesfile=%u", &enabled);
-        g_free (output);
-        if (scanned != 1)
-            return FALSE;
-
-        return enabled;
-    } else {
-        g_clear_error (&loc_error);
-        g_free (output);
-    }
-
-    return FALSE;
-}
-
-/**
- * bd_lvm_devices_add:
- * @device: device (PV) to add to the devices file
- * @devices_file: (nullable): LVM devices file or %NULL for default
- * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @device was successfully added to @devices_file or not
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"lvmdevices", "--adddev", device, NULL, NULL};
-    g_autofree gchar *devfile = NULL;
-
-    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
-        return FALSE;
-
-    if (!_lvm_devices_enabled ()) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
-                     "LVM devices file not enabled.");
-        return FALSE;
-    }
-
-    if (devices_file) {
-        devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
-        args[3] = devfile;
-    }
-
-    return bd_utils_exec_and_report_error (args, extra, error);
-}
-
-/**
- * bd_lvm_devices_delete:
- * @device: device (PV) to delete from the devices file
- * @devices_file: (nullable): LVM devices file or %NULL for default
- * @extra: (nullable) (array zero-terminated=1): extra options for the lvmdevices command
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: whether the @device was successfully removed from @devices_file or not
- *
- * Tech category: %BD_LVM_TECH_DEVICES no mode (it is ignored)
- */
-gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error) {
-    const gchar *args[5] = {"lvmdevices", "--deldev", device, NULL, NULL};
-    g_autofree gchar *devfile = NULL;
-
-    if (!bd_lvm_is_tech_avail (BD_LVM_TECH_DEVICES, 0, error))
-        return FALSE;
-
-    if (!_lvm_devices_enabled ()) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DEVICES_DISABLED,
-                     "LVM devices file not enabled.");
-        return FALSE;
-    }
-
-    if (devices_file) {
-        devfile = g_strdup_printf ("--devicesfile=%s", devices_file);
-        args[3] = devfile;
-    }
-
-    return bd_utils_exec_and_report_error (args, extra, error);
-}
-
-/**
- * bd_lvm_config_get:
- * @section: (nullable): LVM config section, e.g. 'global' or %NULL to print the entire config
- * @setting: (nullable): name of the specific setting, e.g. 'umask' or %NULL to print the entire @section
- * @type: type of the config, e.g. 'full' or 'current'
- * @values_only: whether to include only values without keys in the output
- * @global_config: whether to include our internal global config in the call or not
- * @extra: (nullable) (array zero-terminated=1): extra options for the lvmconfig command
- *                                               (just passed to LVM as is)
- * @error: (out) (optional): place to store error (if any)
- *
- * Returns: (transfer full): Requested LVM config @section and @setting configuration or %NULL in case of error.
- *
- * Tech category: %BD_LVM_TECH_CONFIG no mode (it is ignored)
- */
-gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error) {
-    g_autofree gchar *conf_spec = NULL;
-    g_autofree gchar *config_arg = NULL;
-    const gchar *args[7] = {"lvmconfig", "--typeconfig", NULL, NULL, NULL, NULL, NULL};
-    guint next_arg = 2;
-    gchar *output = NULL;
-    gboolean success = FALSE;
-
-    if (!section && setting) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_FAIL,
-                     "Specifying setting without section is not supported.");
-        return NULL;
-    }
-
-    if (section)
-        if (setting)
-            conf_spec = g_strdup_printf ("%s/%s", section, setting);
-        else
-            conf_spec = g_strdup (section);
-    else
-        conf_spec = NULL;
-
-    args[next_arg++] = type;
-    args[next_arg++] = conf_spec;
-    if (values_only)
-        args[next_arg++] = "--valuesonly";
-
-    g_mutex_lock (&global_config_lock);
-    if (global_config && global_config_str) {
-        config_arg = g_strdup_printf ("--config=%s", global_config_str);
-        args[next_arg++] = config_arg;
-    }
-    g_mutex_unlock (&global_config_lock);
-
-    success = bd_utils_exec_and_capture_output (args, extra, &output, error);
-    if (!success)
-        return NULL;
-    return g_strchomp (output);
-}
diff -pruN 3.3.1-3/src/plugins/lvm.h 3.4.0-1/src/plugins/lvm.h
--- 3.3.1-3/src/plugins/lvm.h	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/lvm.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,336 +0,0 @@
-#include <glib.h>
-#include <blockdev/utils.h>
-
-#ifndef BD_LVM
-#define BD_LVM
-
-GQuark bd_lvm_error_quark (void);
-#define BD_LVM_ERROR bd_lvm_error_quark ()
-typedef enum {
-    BD_LVM_ERROR_TECH_UNAVAIL,
-    BD_LVM_ERROR_FAIL,
-    BD_LVM_ERROR_PARSE,
-    BD_LVM_ERROR_NOEXIST,
-    BD_LVM_ERROR_DM_ERROR,
-    BD_LVM_ERROR_NOT_ROOT,
-    BD_LVM_ERROR_CACHE_INVAL,
-    BD_LVM_ERROR_CACHE_NOCACHE,
-    BD_LVM_ERROR_NOT_SUPPORTED,
-    BD_LVM_ERROR_VDO_POLICY_INVAL,
-    BD_LVM_ERROR_DEVICES_DISABLED,
-} BDLVMError;
-
-typedef enum {
-    BD_LVM_CACHE_POOL_STRIPED =  1 << 0,
-    BD_LVM_CACHE_POOL_RAID1 =    1 << 1,
-    BD_LVM_CACHE_POOL_RAID5 =    1 << 2,
-    BD_LVM_CACHE_POOL_RAID6 =    1 << 3,
-    BD_LVM_CACHE_POOL_RAID10 =   1 << 4,
-
-    BD_LVM_CACHE_POOL_META_STRIPED =  1 << 10,
-    BD_LVM_CACHE_POOL_META_RAID1 =    1 << 11,
-    BD_LVM_CACHE_POOL_META_RAID5 =    1 << 12,
-    BD_LVM_CACHE_POOL_META_RAID6 =    1 << 13,
-    BD_LVM_CACHE_POOL_META_RAID10 =   1 << 14,
-} BDLVMCachePoolFlags;
-
-typedef enum {
-    BD_LVM_CACHE_MODE_UNKNOWN,
-    BD_LVM_CACHE_MODE_WRITETHROUGH,
-    BD_LVM_CACHE_MODE_WRITEBACK,
-} BDLVMCacheMode;
-
-typedef enum {
-    BD_LVM_VDO_MODE_UNKNOWN,
-    BD_LVM_VDO_MODE_RECOVERING,
-    BD_LVM_VDO_MODE_READ_ONLY,
-    BD_LVM_VDO_MODE_NORMAL,
-} BDLVMVDOOperatingMode;
-
-typedef enum {
-    BD_LVM_VDO_COMPRESSION_UNKNOWN,
-    BD_LVM_VDO_COMPRESSION_ONLINE,
-    BD_LVM_VDO_COMPRESSION_OFFLINE,
-} BDLVMVDOCompressionState;
-
-typedef enum {
-    BD_LVM_VDO_INDEX_UNKNOWN,
-    BD_LVM_VDO_INDEX_ERROR,
-    BD_LVM_VDO_INDEX_CLOSED,
-    BD_LVM_VDO_INDEX_OPENING,
-    BD_LVM_VDO_INDEX_CLOSING,
-    BD_LVM_VDO_INDEX_OFFLINE,
-    BD_LVM_VDO_INDEX_ONLINE,
-} BDLVMVDOIndexState;
-
-typedef enum {
-    BD_LVM_VDO_WRITE_POLICY_UNKNOWN,
-    BD_LVM_VDO_WRITE_POLICY_AUTO,
-    BD_LVM_VDO_WRITE_POLICY_SYNC,
-    BD_LVM_VDO_WRITE_POLICY_ASYNC,
-} BDLVMVDOWritePolicy;
-
-typedef struct BDLVMPVdata {
-    gchar *pv_name;
-    gchar *pv_uuid;
-    guint64 pv_free;
-    guint64 pv_size;
-    guint64 pe_start;
-    gchar *vg_name;
-    gchar *vg_uuid;
-    guint64 vg_size;
-    guint64 vg_free;
-    guint64 vg_extent_size;
-    guint64 vg_extent_count;
-    guint64 vg_free_count;
-    guint64 vg_pv_count;
-    gchar **pv_tags;
-    gboolean missing;
-} BDLVMPVdata;
-
-void bd_lvm_pvdata_free (BDLVMPVdata *data);
-BDLVMPVdata* bd_lvm_pvdata_copy (BDLVMPVdata *data);
-
-typedef struct BDLVMVGdata {
-    gchar *name;
-    gchar *uuid;
-    guint64 size;
-    guint64 free;
-    guint64 extent_size;
-    guint64 extent_count;
-    guint64 free_count;
-    guint64 pv_count;
-    gboolean exported;
-    gchar **vg_tags;
-} BDLVMVGdata;
-
-void bd_lvm_vgdata_free (BDLVMVGdata *data);
-BDLVMVGdata* bd_lvm_vgdata_copy (BDLVMVGdata *data);
-
-typedef struct BDLVMSEGdata {
-    guint64 size_pe;
-    guint64 pv_start_pe;
-    gchar *pvdev;
-} BDLVMSEGdata;
-
-BDLVMSEGdata* bd_lvm_segdata_copy (BDLVMSEGdata *data);
-void bd_lvm_segdata_free (BDLVMSEGdata *data);
-
-typedef struct BDLVMLVdata {
-    gchar *lv_name;
-    gchar *vg_name;
-    gchar *uuid;
-    guint64 size;
-    gchar *attr;
-    gchar *segtype;
-    gchar *origin;
-    gchar *pool_lv;
-    gchar *data_lv;
-    gchar *metadata_lv;
-    gchar *roles;
-    gchar *move_pv;
-    guint64 data_percent;
-    guint64 metadata_percent;
-    guint64 copy_percent;
-    gchar **lv_tags;
-    gchar **data_lvs;
-    gchar **metadata_lvs;
-    BDLVMSEGdata **segs;
-} BDLVMLVdata;
-
-void bd_lvm_lvdata_free (BDLVMLVdata *data);
-BDLVMLVdata* bd_lvm_lvdata_copy (BDLVMLVdata *data);
-
-typedef struct BDLVMVDOPooldata {
-    BDLVMVDOOperatingMode operating_mode;
-    BDLVMVDOCompressionState compression_state;
-    BDLVMVDOIndexState index_state;
-    BDLVMVDOWritePolicy write_policy;
-    guint64 used_size;
-    gint32 saving_percent;
-    guint64 index_memory_size;
-    gboolean deduplication;
-    gboolean compression;
-} BDLVMVDOPooldata;
-
-void bd_lvm_vdopooldata_free (BDLVMVDOPooldata *data);
-BDLVMVDOPooldata* bd_lvm_vdopooldata_copy (BDLVMVDOPooldata *data);
-
-typedef struct BDLVMVDOStats {
-    gint64 block_size;
-    gint64 logical_block_size;
-    gint64 physical_blocks;
-    gint64 data_blocks_used;
-    gint64 overhead_blocks_used;
-    gint64 logical_blocks_used;
-    gint64 used_percent;
-    gint64 saving_percent;
-    gdouble write_amplification_ratio;
-} BDLVMVDOStats;
-
-void bd_lvm_vdo_stats_free (BDLVMVDOStats *stats);
-BDLVMVDOStats* bd_lvm_vdo_stats_copy (BDLVMVDOStats *stats);
-
-typedef struct BDLVMCacheStats {
-    guint64 block_size;
-    guint64 cache_size;
-    guint64 cache_used;
-    guint64 md_block_size;
-    guint64 md_size;
-    guint64 md_used;
-    guint64 read_hits;
-    guint64 read_misses;
-    guint64 write_hits;
-    guint64 write_misses;
-    BDLVMCacheMode mode;
-} BDLVMCacheStats;
-
-void bd_lvm_cache_stats_free (BDLVMCacheStats *data);
-BDLVMCacheStats* bd_lvm_cache_stats_copy (BDLVMCacheStats *data);
-
-typedef enum {
-    BD_LVM_TECH_BASIC = 0,
-    BD_LVM_TECH_BASIC_SNAP,
-    BD_LVM_TECH_THIN,
-    BD_LVM_TECH_CACHE,
-    BD_LVM_TECH_CALCS,
-    BD_LVM_TECH_THIN_CALCS,
-    BD_LVM_TECH_CACHE_CALCS,
-    BD_LVM_TECH_GLOB_CONF,
-    BD_LVM_TECH_VDO,
-    BD_LVM_TECH_WRITECACHE,
-    BD_LVM_TECH_DEVICES,
-    BD_LVM_TECH_SHARED,
-    BD_LVM_TECH_CONFIG,
-} BDLVMTech;
-
-typedef enum {
-    BD_LVM_TECH_MODE_CREATE = 1 << 0,
-    BD_LVM_TECH_MODE_REMOVE = 1 << 2,
-    BD_LVM_TECH_MODE_MODIFY = 1 << 3,
-    BD_LVM_TECH_MODE_QUERY  = 1 << 4,
-} BDLVMTechMode;
-
-
-/*
- * If using the plugin as a standalone library, the following functions should
- * be called to:
- *
- * init()       - initialize the plugin, returning TRUE on success
- * close()      - clean after the plugin at the end or if no longer used
- *
- */
-gboolean bd_lvm_init (void);
-void bd_lvm_close (void);
-
-gboolean bd_lvm_is_tech_avail (BDLVMTech tech, guint64 mode, GError **error);
-
-gboolean bd_lvm_is_supported_pe_size (guint64 size, GError **error);
-guint64 *bd_lvm_get_supported_pe_sizes (GError **error);
-guint64 bd_lvm_get_max_lv_size (GError **error);
-guint64 bd_lvm_round_size_to_pe (guint64 size, guint64 pe_size, gboolean roundup, GError **error);
-guint64 bd_lvm_get_lv_physical_size (guint64 lv_size, guint64 pe_size, GError **error);
-guint64 bd_lvm_get_thpool_padding (guint64 size, guint64 pe_size, gboolean included, GError **error);
-guint64 bd_lvm_get_thpool_meta_size (guint64 size, guint64 chunk_size, guint64 n_snapshots, GError **error);
-gboolean bd_lvm_is_valid_thpool_md_size (guint64 size, GError **error);
-gboolean bd_lvm_is_valid_thpool_chunk_size (guint64 size, gboolean discard, GError **error);
-
-gboolean bd_lvm_pvcreate (const gchar *device, guint64 data_alignment, guint64 metadata_size, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_pvresize (const gchar *device, guint64 size, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_pvremove (const gchar *device, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_pvmove (const gchar *src, const gchar *dest, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_pvscan (const gchar *device, gboolean update_cache, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_add_pv_tags (const gchar *device, const gchar **tags, GError **error);
-gboolean bd_lvm_delete_pv_tags (const gchar *device, const gchar **tags, GError **error);
-BDLVMPVdata* bd_lvm_pvinfo (const gchar *device, GError **error);
-BDLVMPVdata** bd_lvm_pvs (GError **error);
-
-gboolean bd_lvm_vgcreate (const gchar *name, const gchar **pv_list, guint64 pe_size, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vgremove (const gchar *vg_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vgrename (const gchar *old_vg_name, const gchar *new_vg_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vgactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vgdeactivate (const gchar *vg_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vgextend (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vgreduce (const gchar *vg_name, const gchar *device, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_add_vg_tags (const gchar *vg_name, const gchar **tags, GError **error);
-gboolean bd_lvm_delete_vg_tags (const gchar *vg_name, const gchar **tags, GError **error);
-gboolean bd_lvm_vglock_start (const gchar *vg_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vglock_stop (const gchar *vg_name, const BDExtraArg **extra, GError **error);
-BDLVMVGdata* bd_lvm_vginfo (const gchar *vg_name, GError **error);
-BDLVMVGdata** bd_lvm_vgs (GError **error);
-
-gchar* bd_lvm_lvorigin (const gchar *vg_name, const gchar *lv_name, GError **error);
-gboolean bd_lvm_lvcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, const gchar *type, const gchar **pv_list, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvremove (const gchar *vg_name, const gchar *lv_name, gboolean force, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvrename (const gchar *vg_name, const gchar *lv_name, const gchar *new_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvresize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvrepair (const gchar *vg_name, const gchar *lv_name, const gchar **pv_list, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvactivate (const gchar *vg_name, const gchar *lv_name, gboolean ignore_skip, gboolean shared, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvdeactivate (const gchar *vg_name, const gchar *lv_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, guint64 size, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_lvsnapshotmerge (const gchar *vg_name, const gchar *snapshot_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_add_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error);
-gboolean bd_lvm_delete_lv_tags (const gchar *vg_name, const gchar *lv_name, const gchar **tags, GError **error);
-BDLVMLVdata* bd_lvm_lvinfo (const gchar *vg_name, const gchar *lv_name, GError **error);
-BDLVMLVdata* bd_lvm_lvinfo_tree (const gchar *vg_name, const gchar *lv_name, GError **error);
-BDLVMLVdata** bd_lvm_lvs (const gchar *vg_name, GError **error);
-BDLVMLVdata** bd_lvm_lvs_tree (const gchar *vg_name, GError **error);
-
-gboolean bd_lvm_thpoolcreate (const gchar *vg_name, const gchar *lv_name, guint64 size, guint64 md_size, guint64 chunk_size, const gchar *profile, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_thlvcreate (const gchar *vg_name, const gchar *pool_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error);
-gchar* bd_lvm_thlvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error);
-gboolean bd_lvm_thsnapshotcreate (const gchar *vg_name, const gchar *origin_name, const gchar *snapshot_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
-
-gboolean bd_lvm_set_global_config (const gchar *new_config, GError **error);
-gchar* bd_lvm_get_global_config (GError **error);
-
-gboolean bd_lvm_set_devices_filter (const gchar **devices, GError **error);
-gchar** bd_lvm_get_devices_filter (GError **error);
-
-guint64 bd_lvm_cache_get_default_md_size (guint64 cache_size, GError **error);
-const gchar* bd_lvm_cache_get_mode_str (BDLVMCacheMode mode, GError **error);
-BDLVMCacheMode bd_lvm_cache_get_mode_from_str (const gchar *mode_str, GError **error);
-gboolean bd_lvm_cache_create_pool (const gchar *vg_name, const gchar *pool_name, guint64 pool_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags, const gchar **fast_pvs, GError **error);
-gboolean bd_lvm_cache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_pool_lv, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_cache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_cache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, guint64 md_size, BDLVMCacheMode mode, BDLVMCachePoolFlags flags,
-                                        const gchar **slow_pvs, const gchar **fast_pvs, GError **error);
-gchar* bd_lvm_cache_pool_name (const gchar *vg_name, const gchar *cached_lv, GError **error);
-BDLVMCacheStats* bd_lvm_cache_stats (const gchar *vg_name, const gchar *cached_lv, GError **error);
-
-gboolean bd_lvm_writecache_attach (const gchar *vg_name, const gchar *data_lv, const gchar *cache_lv, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_writecache_detach (const gchar *vg_name, const gchar *cached_lv, gboolean destroy, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_writecache_create_cached_lv (const gchar *vg_name, const gchar *lv_name, guint64 data_size, guint64 cache_size, const gchar **slow_pvs, const gchar **fast_pvs, GError **error);
-
-gboolean bd_lvm_vdo_pool_create (const gchar *vg_name, const gchar *lv_name, const gchar *pool_name, guint64 data_size, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error);
-BDLVMVDOPooldata *bd_lvm_vdo_info (const gchar *vg_name, const gchar *lv_name, GError **error);
-
-gboolean bd_lvm_vdo_resize (const gchar *vg_name, const gchar *lv_name, guint64 size, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vdo_pool_resize (const gchar *vg_name, const gchar *pool_name, guint64 size, const BDExtraArg **extra, GError **error);
-
-gboolean bd_lvm_vdo_enable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vdo_disable_compression (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vdo_enable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vdo_disable_deduplication (const gchar *vg_name, const gchar *pool_name, const BDExtraArg **extra, GError **error);
-
-gboolean bd_lvm_thpool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_cache_pool_convert (const gchar *vg_name, const gchar *data_lv, const gchar *metadata_lv, const gchar *name, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_vdo_pool_convert (const gchar *vg_name, const gchar *pool_lv, const gchar *name, guint64 virtual_size, guint64 index_memory, gboolean compression, gboolean deduplication, BDLVMVDOWritePolicy write_policy, const BDExtraArg **extra, GError **error);
-gchar* bd_lvm_vdolvpoolname (const gchar *vg_name, const gchar *lv_name, GError **error);
-
-const gchar* bd_lvm_get_vdo_operating_mode_str (BDLVMVDOOperatingMode mode, GError **error);
-const gchar* bd_lvm_get_vdo_compression_state_str (BDLVMVDOCompressionState state, GError **error);
-const gchar* bd_lvm_get_vdo_index_state_str (BDLVMVDOIndexState state, GError **error);
-const gchar* bd_lvm_get_vdo_write_policy_str (BDLVMVDOWritePolicy policy, GError **error);
-
-BDLVMVDOWritePolicy bd_lvm_get_vdo_write_policy_from_str (const gchar *policy_str, GError **error);
-
-BDLVMVDOStats* bd_lvm_vdo_get_stats (const gchar *vg_name, const gchar *pool_name, GError **error);
-GHashTable* bd_lvm_vdo_get_stats_full (const gchar *vg_name, const gchar *pool_name, GError **error);
-
-gboolean bd_lvm_devices_add (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
-gboolean bd_lvm_devices_delete (const gchar *device, const gchar *devices_file, const BDExtraArg **extra, GError **error);
-
-gchar* bd_lvm_config_get (const gchar *section, const gchar *setting, const gchar *type, gboolean values_only, gboolean global_config, const BDExtraArg **extra, GError **error);
-
-#endif /* BD_LVM */
diff -pruN 3.3.1-3/src/plugins/nvme/nvme-fabrics.c 3.4.0-1/src/plugins/nvme/nvme-fabrics.c
--- 3.3.1-3/src/plugins/nvme/nvme-fabrics.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/nvme/nvme-fabrics.c	2025-09-24 13:46:42.000000000 +0000
@@ -156,7 +156,7 @@ static void parse_extra_args (const BDEx
  * @subsysnqn: The name for the NVMe subsystem to connect to.
  * @transport: The network fabric used for a NVMe-over-Fabrics network.
  * @transport_addr: (nullable): The network address of the Controller. For transports using IP addressing (e.g. `rdma`) this should be an IP-based address.
- * @transport_svcid: (nullable): The transport service id.  For transports using IP addressing (e.g. `rdma`) this field is the port number. By default, the IP port number for the `RDMA` transport is `4420`.
+ * @transport_svcid: (nullable): The transport service ID.  For transports using IP addressing (e.g. `tcp`, `rdma`) this field is the port number. The default port number for the `tcp` and `rdma` transports is `4420` and `8009` respectively when the well-known Discovery NQN is specified.
  * @host_traddr: (nullable): The network address used on the host to connect to the Controller. For TCP, this sets the source address on the socket.
  * @host_iface: (nullable): The network interface used on the host to connect to the Controller (e.g. IP `eth1`, `enp2s0`). This forces the connection to be made on a specific interface instead of letting the system decide.
  * @host_nqn: (nullable): Overrides the default Host NQN that identifies the NVMe Host. If this option is %NULL, the default is read from `/etc/nvme/hostnqn` first.
@@ -300,6 +300,18 @@ gboolean bd_nvme_connect (const gchar *s
     if (hostsymname)
         nvme_host_set_hostsymname (host, hostsymname);
 
+    /* tr_svcid defaults */
+    if (!transport_svcid) {
+        if (g_strcmp0 (transport, "tcp") == 0) {
+            if (g_strcmp0 (subsysnqn, NVME_DISC_SUBSYS_NAME) == 0)
+                transport_svcid = G_STRINGIFY (NVME_DISC_IP_PORT);
+            else
+                transport_svcid = G_STRINGIFY (NVME_RDMA_IP_PORT);
+        } else
+        if (g_strcmp0(transport, "rdma") == 0)
+            transport_svcid = G_STRINGIFY (NVME_RDMA_IP_PORT);
+    }
+
     ctrl = nvme_create_ctrl (root, subsysnqn, transport, transport_addr, host_traddr, host_iface, transport_svcid);
     if (ctrl == NULL) {
         _nvme_fabrics_errno_to_gerror (-1, errno, error);
diff -pruN 3.3.1-3/src/plugins/nvme/nvme-info.c 3.4.0-1/src/plugins/nvme/nvme-info.c
--- 3.3.1-3/src/plugins/nvme/nvme-info.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/nvme/nvme-info.c	2025-09-24 13:46:42.000000000 +0000
@@ -405,13 +405,16 @@ gint _open_dev (const gchar *device, GEr
 /* backported from nvme-cli: https://github.com/linux-nvme/nvme-cli/pull/2051 */
 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
 
-void *_nvme_alloc (size_t len)
+void *_nvme_alloc (size_t len, GError **error)
 {
     size_t _len = ROUND_UP (len, 0x1000);
     void *p;
 
-    if (posix_memalign ((void *) &p, getpagesize (), _len))
+    if (posix_memalign ((void *) &p, getpagesize (), _len)) {
+        g_set_error (error, BD_NVME_ERROR, BD_NVME_ERROR_FAILED,
+                     "Memory allocation failed: %m");
         return NULL;
+    }
 
     memset (p, 0, _len);
     return p;
@@ -475,8 +478,9 @@ BDNVMEControllerInfo * bd_nvme_get_contr
     if (fd < 0)
         return NULL;
 
-    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl));
-    g_warn_if_fail (ctrl_id != NULL);
+    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
+    if (!ctrl_id)
+        return NULL;
     /* send the NVME_IDENTIFY_CNS_CTRL ioctl */
     ret = nvme_identify_ctrl (fd, ctrl_id);
     if (ret != 0) {
@@ -616,8 +620,11 @@ BDNVMENamespaceInfo *bd_nvme_get_namespa
     }
 
     /* send the NVME_IDENTIFY_CNS_NS ioctl */
-    ns_info = _nvme_alloc (sizeof (struct nvme_id_ns));
-    g_warn_if_fail (ns_info != NULL);
+    ns_info = _nvme_alloc (sizeof (struct nvme_id_ns), error);
+    if (!ns_info) {
+        close (fd);
+        return NULL;
+    }
     ret = nvme_identify_ns (fd, nsid, ns_info);
     if (ret != 0) {
         _nvme_status_to_error (ret, FALSE, error);
@@ -628,22 +635,26 @@ BDNVMENamespaceInfo *bd_nvme_get_namespa
     }
 
     /* send the NVME_IDENTIFY_CNS_CTRL ioctl */
-    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl));
-    g_warn_if_fail (ctrl_id != NULL);
+    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
+    if (!ctrl_id) {
+        close (fd);
+        free (ns_info);
+        return NULL;
+    }
     ret_ctrl = nvme_identify_ctrl (fd, ctrl_id);
 
     /* send the NVME_IDENTIFY_CNS_NS_DESC_LIST ioctl, NVMe 1.3 */
     if (ret_ctrl == 0 && GUINT32_FROM_LE (ctrl_id->ver) >= 0x10300) {
-        descs = _nvme_alloc (NVME_IDENTIFY_DATA_SIZE);
-        g_warn_if_fail (descs != NULL);
-        ret_desc = nvme_identify_ns_descs (fd, nsid, descs);
+        descs = _nvme_alloc (NVME_IDENTIFY_DATA_SIZE, NULL);
+        if (descs != NULL)
+            ret_desc = nvme_identify_ns_descs (fd, nsid, descs);
     }
 
     /* send the NVME_IDENTIFY_CNS_CSI_INDEPENDENT_ID_NS ioctl, NVMe 2.0 */
     if (ret_ctrl == 0 && GUINT32_FROM_LE (ctrl_id->ver) >= 0x20000) {
-        ns_info_ind = _nvme_alloc (sizeof (struct nvme_id_independent_id_ns));
-        g_warn_if_fail (ns_info_ind != NULL);
-        ret_ns_ind = nvme_identify_independent_identify_ns (fd, nsid, ns_info_ind);
+        ns_info_ind = _nvme_alloc (sizeof (struct nvme_id_independent_id_ns), NULL);
+        if (ns_info_ind != NULL)
+            ret_ns_ind = nvme_identify_independent_identify_ns (fd, nsid, ns_info_ind);
     }
     close (fd);
 
@@ -762,8 +773,11 @@ BDNVMESmartLog * bd_nvme_get_smart_log (
         return NULL;
 
     /* send the NVME_IDENTIFY_CNS_CTRL ioctl */
-    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl));
-    g_warn_if_fail (ctrl_id != NULL);
+    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
+    if (!ctrl_id) {
+        close (fd);
+        return NULL;
+    }
     ret_identify = nvme_identify_ctrl (fd, ctrl_id);
     if (ret_identify != 0) {
         _nvme_status_to_error (ret_identify, FALSE, error);
@@ -774,8 +788,12 @@ BDNVMESmartLog * bd_nvme_get_smart_log (
     }
 
     /* send the NVME_LOG_LID_SMART ioctl */
-    smart_log = _nvme_alloc (sizeof (struct nvme_smart_log));
-    g_warn_if_fail (smart_log != NULL);
+    smart_log = _nvme_alloc (sizeof (struct nvme_smart_log), error);
+    if (!smart_log) {
+        close (fd);
+        free (ctrl_id);
+        return NULL;
+    }
     ret = nvme_get_log_smart (fd, NVME_NSID_ALL, FALSE /* rae */, smart_log);
     if (ret != 0) {
         _nvme_status_to_error (ret, FALSE, error);
@@ -867,8 +885,11 @@ BDNVMEErrorLogEntry ** bd_nvme_get_error
         return NULL;
 
     /* find out the maximum number of error log entries as reported by the controller */
-    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl));
-    g_warn_if_fail (ctrl_id != NULL);
+    ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
+    if (!ctrl_id) {
+        close (fd);
+        return NULL;
+    }
     ret = nvme_identify_ctrl (fd, ctrl_id);
     if (ret != 0) {
         _nvme_status_to_error (ret, FALSE, error);
@@ -882,8 +903,11 @@ BDNVMEErrorLogEntry ** bd_nvme_get_error
     free (ctrl_id);
 
     /* send the NVME_LOG_LID_ERROR ioctl */
-    err_log = _nvme_alloc (sizeof (struct nvme_error_log_page) * elpe);
-    g_warn_if_fail (err_log != NULL);
+    err_log = _nvme_alloc (sizeof (struct nvme_error_log_page) * elpe, error);
+    if (!err_log) {
+        close (fd);
+        return NULL;
+    }
     ret = nvme_get_log_error (fd, elpe, FALSE /* rae */, err_log);
     if (ret != 0) {
         _nvme_status_to_error (ret, FALSE, error);
@@ -948,8 +972,11 @@ BDNVMESelfTestLog * bd_nvme_get_self_tes
         return NULL;
 
     /* send the NVME_LOG_LID_DEVICE_SELF_TEST ioctl */
-    self_test_log = _nvme_alloc (sizeof (struct nvme_self_test_log));
-    g_warn_if_fail (self_test_log != NULL);
+    self_test_log = _nvme_alloc (sizeof (struct nvme_self_test_log), error);
+    if (!self_test_log) {
+        close (fd);
+        return NULL;
+    }
     ret = nvme_get_log_device_self_test (fd, self_test_log);
     if (ret != 0) {
         _nvme_status_to_error (ret, FALSE, error);
@@ -1092,8 +1119,11 @@ BDNVMESanitizeLog * bd_nvme_get_sanitize
         return NULL;
 
     /* send the NVME_LOG_LID_SANITIZE ioctl */
-    sanitize_log = _nvme_alloc (sizeof (struct nvme_sanitize_log_page));
-    g_warn_if_fail (sanitize_log != NULL);
+    sanitize_log = _nvme_alloc (sizeof (struct nvme_sanitize_log_page), error);
+    if (!sanitize_log) {
+        close (fd);
+        return NULL;
+    }
     ret = nvme_get_log_sanitize (fd, FALSE /* rae */, sanitize_log);
     if (ret != 0) {
         _nvme_status_to_error (ret, FALSE, error);
diff -pruN 3.3.1-3/src/plugins/nvme/nvme-op.c 3.4.0-1/src/plugins/nvme/nvme-op.c
--- 3.3.1-3/src/plugins/nvme/nvme-op.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/nvme/nvme-op.c	2025-09-24 13:46:42.000000000 +0000
@@ -121,8 +121,9 @@ static __u8 find_lbaf_for_size (int fd,
     guint i;
 
     /* TODO: find first attached namespace instead of hardcoding NSID = 1 */
-    ns_info = _nvme_alloc (sizeof (struct nvme_id_ns));
-    g_warn_if_fail (ns_info != NULL);
+    ns_info = _nvme_alloc (sizeof (struct nvme_id_ns), error);
+    if (!ns_info)
+        return 0xff;
     ret = nvme_identify_ns (fd, nsid == 0xffffffff ? 1 : nsid, ns_info);
     if (ret != 0) {
         _nvme_status_to_error (ret, FALSE, error);
@@ -214,8 +215,11 @@ gboolean bd_nvme_format (const gchar *de
 
     /* check the FNA controller bit when formatting a single namespace */
     if (! ctrl_device) {
-        ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl));
-        g_warn_if_fail (ctrl_id != NULL);
+        ctrl_id = _nvme_alloc (sizeof (struct nvme_id_ctrl), error);
+        if (!ctrl_id) {
+            close (args.fd);
+            return FALSE;
+        }
         ret = nvme_identify_ctrl (args.fd, ctrl_id);
         if (ret != 0) {
             _nvme_status_to_error (ret, FALSE, error);
diff -pruN 3.3.1-3/src/plugins/nvme/nvme-private.h 3.4.0-1/src/plugins/nvme/nvme-private.h
--- 3.3.1-3/src/plugins/nvme/nvme-private.h	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/nvme/nvme-private.h	2025-09-24 13:46:42.000000000 +0000
@@ -25,6 +25,6 @@ void _nvme_fabrics_errno_to_gerror (int
 G_GNUC_INTERNAL
 gint _open_dev (const gchar *device, GError **error);
 G_GNUC_INTERNAL
-void *_nvme_alloc (size_t len);
+void *_nvme_alloc (size_t len, GError **error);
 
 #endif  /* BD_NVME_PRIVATE */
diff -pruN 3.3.1-3/src/plugins/s390.c 3.4.0-1/src/plugins/s390.c
--- 3.3.1-3/src/plugins/s390.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/s390.c	2025-09-24 13:46:42.000000000 +0000
@@ -790,10 +790,20 @@ gboolean bd_s390_zfcp_scsi_offline (cons
         hba_path = g_strdup_printf ("%s/hba_id", fcpsysfs);
         len = 0; /* should be zero, but re-set it just in case */
         fd = fopen (hba_path, "r");
+        if (!fd) {
+            g_set_error (&l_error, BD_S390_ERROR, BD_S390_ERROR_DEVICE,
+                         "Failed to open %s: %m", hba_path);
+            g_free (hba_path);
+            g_free (fcpsysfs);
+            g_free (scsidev);
+            bd_utils_report_finished (progress_id, l_error->message);
+            g_propagate_error (error, l_error);
+            return FALSE;
+        }
         rc = getline (&fcphbasysfs, &len, fd);
         if (rc == -1) {
             g_set_error (&l_error, BD_S390_ERROR, BD_S390_ERROR_DEVICE,
-                         "Failed to read value from %s", hba_path);
+                         "Failed to read value from %s: %m", hba_path);
             fclose (fd);
             g_free (hba_path);
             g_free (fcpsysfs);
@@ -809,6 +819,17 @@ gboolean bd_s390_zfcp_scsi_offline (cons
         wwpn_path = g_strdup_printf ("%s/wwpn", fcpsysfs);
         len = 0;
         fd = fopen (wwpn_path, "r");
+        if (!fd) {
+            g_set_error (&l_error, BD_S390_ERROR, BD_S390_ERROR_DEVICE,
+                         "Failed to open %s: %m", wwpn_path);
+            g_free (wwpn_path);
+            g_free (fcphbasysfs);
+            g_free (fcpsysfs);
+            g_free (scsidev);
+            bd_utils_report_finished (progress_id, l_error->message);
+            g_propagate_error (error, l_error);
+            return FALSE;
+        }
         rc = getline (&fcpwwpnsysfs, &len, fd);
         if (rc == -1) {
             g_set_error (&l_error, BD_S390_ERROR, BD_S390_ERROR_DEVICE,
@@ -829,6 +850,18 @@ gboolean bd_s390_zfcp_scsi_offline (cons
         lun_path = g_strdup_printf ("%s/fcp_lun", fcpsysfs);
         len = 0;
         fd = fopen (lun_path, "r");
+        if (!fd) {
+            g_set_error (&l_error, BD_S390_ERROR, BD_S390_ERROR_DEVICE,
+                         "Failed to open %s: %m", lun_path);
+            g_free (lun_path);
+            g_free (fcpwwpnsysfs);
+            g_free (fcphbasysfs);
+            g_free (fcpsysfs);
+            g_free (scsidev);
+            bd_utils_report_finished (progress_id, l_error->message);
+            g_propagate_error (error, l_error);
+            return FALSE;
+        }
         rc = getline (&fcplunsysfs, &len, fd);
         if (rc == -1) {
             g_set_error (&l_error, BD_S390_ERROR, BD_S390_ERROR_DEVICE,
diff -pruN 3.3.1-3/src/plugins/smart/libatasmart.c 3.4.0-1/src/plugins/smart/libatasmart.c
--- 3.3.1-3/src/plugins/smart/libatasmart.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/smart/libatasmart.c	2025-09-24 13:46:42.000000000 +0000
@@ -240,7 +240,6 @@ static void parse_attr_cb (G_GNUC_UNUSED
 static BDSmartATA * parse_sk_data (SkDisk *d, GError **error) {
     SkBool good = FALSE;
     SkBool available = FALSE;
-    SkSmartOverall overall = SK_SMART_OVERALL_GOOD;
     uint64_t power_on_msec = 0;
     const SkSmartParsedData *parsed_data;
     BDSmartATA *data;
@@ -276,9 +275,7 @@ static BDSmartATA * parse_sk_data (SkDis
      * sk_disk_smart_read_data() would've already returned an error.
      */
     data->smart_enabled = TRUE;
-
-    sk_disk_smart_get_overall (d, &overall);
-    data->overall_status_passed = overall == SK_SMART_OVERALL_GOOD;
+    data->overall_status_passed = good;
 
     switch (parsed_data->offline_data_collection_status) {
         case SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER:
diff -pruN 3.3.1-3/src/plugins/vdo_stats.c 3.4.0-1/src/plugins/vdo_stats.c
--- 3.3.1-3/src/plugins/vdo_stats.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/vdo_stats.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2020  Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Vojtech Trefny <vtrefny@redhat.com>
- */
-
-#include <glib.h>
-#include <blockdev/utils.h>
-#include <libdevmapper.h>
-#include <yaml.h>
-
-#include "vdo_stats.h"
-#include "lvm.h"
-
-
-G_GNUC_INTERNAL gboolean
-get_stat_val64 (GHashTable *stats, const gchar *key, gint64 *val) {
-    const gchar *s;
-    gchar *endptr = NULL;
-
-    s = g_hash_table_lookup (stats, key);
-    if (s == NULL)
-        return FALSE;
-
-    *val = g_ascii_strtoll (s, &endptr, 0);
-    if (endptr == NULL || *endptr != '\0')
-        return FALSE;
-
-    return TRUE;
-}
-
-G_GNUC_INTERNAL gboolean
-get_stat_val64_default (GHashTable *stats, const gchar *key, gint64 *val, gint64 def) {
-    if (!get_stat_val64 (stats, key, val))
-        *val = def;
-    return TRUE;
-}
-
-G_GNUC_INTERNAL gboolean
-get_stat_val_double (GHashTable *stats, const gchar *key, gdouble *val) {
-    const gchar *s;
-    gchar *endptr = NULL;
-
-    s = g_hash_table_lookup (stats, key);
-    if (s == NULL)
-        return FALSE;
-
-    *val = g_ascii_strtod (s, &endptr);
-    if (endptr == NULL || *endptr != '\0')
-        return FALSE;
-
-    return TRUE;
-}
-
-static void add_write_ampl_r_stats (GHashTable *stats) {
-    gint64 bios_meta_write, bios_out_write, bios_in_write;
-
-    if (! get_stat_val64 (stats, "biosMetaWrite", &bios_meta_write) ||
-        ! get_stat_val64 (stats, "biosOutWrite", &bios_out_write) ||
-        ! get_stat_val64 (stats, "biosInWrite", &bios_in_write))
-        return;
-
-    if (bios_in_write <= 0)
-        g_hash_table_replace (stats, g_strdup ("writeAmplificationRatio"), g_strdup ("0.00"));
-    else
-        g_hash_table_replace (stats,
-                              g_strdup ("writeAmplificationRatio"),
-                              g_strdup_printf ("%.2f", (gfloat) (bios_meta_write + bios_out_write) / (gfloat) bios_in_write));
-}
-
-static void add_block_stats (GHashTable *stats) {
-    gint64 physical_blocks, block_size, data_blocks_used, overhead_blocks_used, logical_blocks_used;
-    gint64 savings;
-
-    if (! get_stat_val64 (stats, "physicalBlocks", &physical_blocks) ||
-        ! get_stat_val64 (stats, "blockSize", &block_size) ||
-        ! get_stat_val64 (stats, "dataBlocksUsed", &data_blocks_used) ||
-        ! get_stat_val64 (stats, "overheadBlocksUsed", &overhead_blocks_used) ||
-        ! get_stat_val64 (stats, "logicalBlocksUsed", &logical_blocks_used))
-        return;
-
-    g_hash_table_replace (stats, g_strdup ("oneKBlocks"), g_strdup_printf ("%"G_GINT64_FORMAT, physical_blocks * block_size / 1024));
-    g_hash_table_replace (stats, g_strdup ("oneKBlocksUsed"), g_strdup_printf ("%"G_GINT64_FORMAT, (data_blocks_used + overhead_blocks_used) * block_size / 1024));
-    g_hash_table_replace (stats, g_strdup ("oneKBlocksAvailable"), g_strdup_printf ("%"G_GINT64_FORMAT, (physical_blocks - data_blocks_used - overhead_blocks_used) * block_size / 1024));
-    g_hash_table_replace (stats, g_strdup ("usedPercent"), g_strdup_printf ("%.0f", 100.0 * (gfloat) (data_blocks_used + overhead_blocks_used) / (gfloat) physical_blocks + 0.5));
-    savings = (logical_blocks_used > 0) ? (gint64) (100.0 * (gfloat) (logical_blocks_used - data_blocks_used) / (gfloat) logical_blocks_used) : 100;
-    g_hash_table_replace (stats, g_strdup ("savings"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
-    if (savings >= 0)
-        g_hash_table_replace (stats, g_strdup ("savingPercent"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
-}
-
-static void add_journal_stats (GHashTable *stats) {
-    gint64 journal_entries_committed, journal_entries_started, journal_entries_written;
-    gint64 journal_blocks_committed, journal_blocks_started, journal_blocks_written;
-
-    if (! get_stat_val64 (stats, "journalEntriesCommitted", &journal_entries_committed) ||
-        ! get_stat_val64 (stats, "journalEntriesStarted", &journal_entries_started) ||
-        ! get_stat_val64 (stats, "journalEntriesWritten", &journal_entries_written) ||
-        ! get_stat_val64 (stats, "journalBlocksCommitted", &journal_blocks_committed) ||
-        ! get_stat_val64 (stats, "journalBlocksStarted", &journal_blocks_started) ||
-        ! get_stat_val64 (stats, "journalBlocksWritten", &journal_blocks_written))
-        return;
-
-    g_hash_table_replace (stats, g_strdup ("journalEntriesBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_started - journal_entries_written));
-    g_hash_table_replace (stats, g_strdup ("journalEntriesWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_written - journal_entries_committed));
-    g_hash_table_replace (stats, g_strdup ("journalBlocksBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_started - journal_blocks_written));
-    g_hash_table_replace (stats, g_strdup ("journalBlocksWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_written - journal_blocks_committed));
-}
-
-static void add_computed_stats (GHashTable *stats) {
-    const gchar *s;
-
-    s = g_hash_table_lookup (stats, "logicalBlockSize");
-    g_hash_table_replace (stats,
-                          g_strdup ("fiveTwelveByteEmulation"),
-                          g_strdup ((g_strcmp0 (s, "512") == 0) ? "true" : "false"));
-
-    add_write_ampl_r_stats (stats);
-    add_block_stats (stats);
-    add_journal_stats (stats);
-}
-
-enum parse_flags {
-  PARSE_NEXT_KEY,
-  PARSE_NEXT_VAL,
-  PARSE_NEXT_IGN,
-};
-
-G_GNUC_INTERNAL GHashTable *
-vdo_get_stats_full (const gchar *name, GError **error) {
-    struct dm_task *dmt = NULL;
-    const gchar *response = NULL;
-    yaml_parser_t parser;
-    yaml_token_t token;
-    GHashTable *stats = NULL;
-    gchar *key = NULL;
-    gsize len = 0;
-    int next_token = PARSE_NEXT_IGN;
-    gchar *prefix = NULL;
-
-    dmt = dm_task_create (DM_DEVICE_TARGET_MSG);
-    if (!dmt) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to create DM task");
-        return NULL;
-    }
-
-    if (!dm_task_set_name (dmt, name)) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to set name for DM task");
-        dm_task_destroy (dmt);
-        return NULL;
-    }
-
-    if (!dm_task_set_message (dmt, "stats")) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to set message for DM task");
-        dm_task_destroy (dmt);
-        return NULL;
-    }
-
-    if (!dm_task_run (dmt)) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to run DM task");
-        dm_task_destroy (dmt);
-        return NULL;
-    }
-
-    response = dm_task_get_message_response (dmt);
-    if (!response) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to get response from the DM task");
-        dm_task_destroy (dmt);
-        return NULL;
-    }
-
-    if (!yaml_parser_initialize (&parser)) {
-        g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
-                     "Failed to get initialize YAML parser");
-        dm_task_destroy (dmt);
-        return NULL;
-    }
-
-    stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-    yaml_parser_set_input_string (&parser, (guchar *) response, strlen (response));
-
-    do {
-        yaml_parser_scan (&parser, &token);
-        switch (token.type) {
-            /* key */
-            case YAML_KEY_TOKEN:
-                next_token = PARSE_NEXT_KEY;
-                break;
-            /* value */
-            case YAML_VALUE_TOKEN:
-                next_token = PARSE_NEXT_VAL;
-                break;
-            /* block mapping */
-            case YAML_BLOCK_MAPPING_START_TOKEN:
-                if (next_token == PARSE_NEXT_VAL)
-                    /* we were expecting to read a key-value pair but this is actually
-                       a block start, so we need to free the key we're not going to use */
-                    g_free (key);
-                break;
-            /* mapping */
-            case YAML_FLOW_MAPPING_START_TOKEN:
-                /* start of flow mapping -> previously read key will be used as prefix
-                   for all keys in the mapping:
-                        previous key: biosInProgress
-                        keys in the mapping: Read, Write...
-                        with prefix: biosInProgressRead, biosInProgressWrite...
-                */
-                prefix = key;
-                break;
-            case YAML_FLOW_MAPPING_END_TOKEN:
-                /* end of flow mapping, discard the prefix used */
-                g_free (prefix);
-                prefix = NULL;
-                break;
-            /* actual data */
-            case YAML_SCALAR_TOKEN:
-                if (next_token == PARSE_NEXT_KEY) {
-                    if (prefix) {
-                        key = g_strdup_printf ("%s%s", prefix, (const gchar *) token.data.scalar.value);
-                        len = strlen (prefix);
-                        /* make sure the key with the prefix is still camelCase */
-                        key[len] = g_ascii_toupper (key[len]);
-                    } else
-                        key = g_strdup ((const gchar *) token.data.scalar.value);
-                } else if (next_token == PARSE_NEXT_VAL) {
-                    gchar *val = g_strdup ((const gchar *) token.data.scalar.value);
-                    g_hash_table_insert (stats, key, val);
-                }
-                break;
-            default:
-                break;
-          }
-
-          if (token.type != YAML_STREAM_END_TOKEN)
-              yaml_token_delete (&token);
-    } while (token.type != YAML_STREAM_END_TOKEN);
-
-    yaml_parser_delete (&parser);
-    dm_task_destroy (dmt);
-
-    if (stats != NULL)
-        add_computed_stats (stats);
-
-    return stats;
-}
diff -pruN 3.3.1-3/src/plugins/vdo_stats.h 3.4.0-1/src/plugins/vdo_stats.h
--- 3.3.1-3/src/plugins/vdo_stats.h	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/plugins/vdo_stats.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Vojtech Trefny <vtrefny@redhat.com>
- */
-
-#include <glib.h>
-
-#ifndef BD_VDO_STATS
-#define BD_VDO_STATS
-
-gboolean get_stat_val_double (GHashTable *stats, const gchar *key, gdouble *val);
-gboolean get_stat_val64 (GHashTable *stats, const gchar *key, gint64 *val);
-gboolean get_stat_val64_default (GHashTable *stats, const gchar *key, gint64 *val, gint64 def);
-
-GHashTable* vdo_get_stats_full (const gchar *name, GError **error);
-
-#endif  /* BD_VDO_STATS */
diff -pruN 3.3.1-3/src/utils/module.c 3.4.0-1/src/utils/module.c
--- 3.3.1-3/src/utils/module.c	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/src/utils/module.c	2025-09-24 13:46:42.000000000 +0000
@@ -274,36 +274,34 @@ static gboolean have_linux_ver = FALSE;
 
 G_LOCK_DEFINE_STATIC (detected_linux_ver);
 
-/**
- * bd_utils_get_linux_version:
- * @error: (out) (optional): place to store error (if any)
- *
- * Retrieves version of currently running Linux kernel. Acts also as an initializer for statically cached data.
- *
- * Returns: (transfer none): Detected Linux kernel version or %NULL in case of an error. The returned value belongs to the library, do not free.
- */
-BDUtilsLinuxVersion * bd_utils_get_linux_version (GError **error) {
+BDUtilsLinuxVersion * _get_linux_version (gboolean lock, GError **error) {
     struct utsname buf;
 
+    if (lock)
+        G_LOCK (detected_linux_ver);
+
     /* return cached value if available */
-    if (have_linux_ver)
+    if (have_linux_ver) {
+        if (lock)
+            G_UNLOCK (detected_linux_ver);
         return &detected_linux_ver;
-
-    G_LOCK (detected_linux_ver);
+    }
 
     memset (&detected_linux_ver, 0, sizeof (BDUtilsLinuxVersion));
 
     if (uname (&buf)) {
         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
                      "Failed to get linux kernel version: %m");
-        G_UNLOCK (detected_linux_ver);
+        if (lock)
+            G_UNLOCK (detected_linux_ver);
         return NULL;
       }
 
     if (g_ascii_strncasecmp (buf.sysname, "Linux", sizeof buf.sysname) != 0) {
         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_INVALID_PLATFORM,
                      "Failed to get kernel version: spurious sysname '%s' detected", buf.sysname);
-        G_UNLOCK (detected_linux_ver);
+        if (lock)
+            G_UNLOCK (detected_linux_ver);
         return NULL;
       }
 
@@ -313,17 +311,30 @@ BDUtilsLinuxVersion * bd_utils_get_linux
                 &detected_linux_ver.micro) < 1) {
         g_set_error (error, BD_UTILS_MODULE_ERROR, BD_UTILS_MODULE_ERROR_FAIL,
                      "Failed to parse kernel version: malformed release string '%s'", buf.release);
-        G_UNLOCK (detected_linux_ver);
+        if (lock)
+            G_UNLOCK (detected_linux_ver);
         return NULL;
     }
 
     have_linux_ver = TRUE;
-    G_UNLOCK (detected_linux_ver);
-
+    if (lock)
+        G_UNLOCK (detected_linux_ver);
     return &detected_linux_ver;
 }
 
 /**
+ * bd_utils_get_linux_version:
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Retrieves version of currently running Linux kernel. Acts also as an initializer for statically cached data.
+ *
+ * Returns: (transfer none): Detected Linux kernel version or %NULL in case of an error. The returned value belongs to the library, do not free.
+ */
+BDUtilsLinuxVersion * bd_utils_get_linux_version (GError **error) {
+    return _get_linux_version (TRUE, error);
+}
+
+/**
  * bd_utils_check_linux_version:
  * @major: Minimal major linux kernel version.
  * @minor: Minimal minor linux kernel version.
@@ -337,10 +348,10 @@ BDUtilsLinuxVersion * bd_utils_get_linux
 gint bd_utils_check_linux_version (guint major, guint minor, guint micro) {
     gint ret;
 
+    G_LOCK (detected_linux_ver);
     if (!have_linux_ver)
-        bd_utils_get_linux_version (NULL);
+        _get_linux_version (FALSE, NULL);
 
-    G_LOCK (detected_linux_ver);
     ret = detected_linux_ver.major - major;
     if (ret == 0)
         ret = detected_linux_ver.minor - minor;
diff -pruN 3.3.1-3/tests/_lvm_cases.py 3.4.0-1/tests/_lvm_cases.py
--- 3.3.1-3/tests/_lvm_cases.py	1970-01-01 00:00:00.000000000 +0000
+++ 3.4.0-1/tests/_lvm_cases.py	2025-09-24 13:46:42.000000000 +0000
@@ -0,0 +1,2194 @@
+import math
+import os
+import re
+import shutil
+import tempfile
+import time
+import unittest
+
+from contextlib import contextmanager
+from packaging.version import Version
+
+import dbus
+
+import overrides_hack
+
+from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, run_command, TestTags, tag_test, read_file
+
+import gi
+gi.require_version('GLib', '2.0')
+gi.require_version('BlockDev', '3.0')
+from gi.repository import GLib, BlockDev
+
+
+def _get_lvm_version():
+    _ret, out, _err = run_command("lvm version")
+    m = re.search(r"LVM version:\s+([\d\.]+)", out)
+    if not m or len(m.groups()) != 1:
+        raise RuntimeError("Failed to determine LVM version from: %s" % out)
+    return Version(m.groups()[0])
+
+LVM_VERSION = _get_lvm_version()
+
+
+@contextmanager
+def wait_for_sync(vg_name, lv_name):
+    try:
+        yield
+    finally:
+        time.sleep(2)
+        while True:
+            ret, out, _err = run_command("LC_ALL=C lvs -o copy_percent --noheadings %s/%s" % (vg_name, lv_name))
+            if ret != 0:
+                break
+            if int(float(out)) == 100:
+                break
+            time.sleep(1)
+
+
+class LvmTestCase(unittest.TestCase):
+    @classmethod
+    def _get_lvm_segtypes(cls):
+        _ret, out, _err = run_command("lvm segtypes")
+        return out
+
+    def _lvremove(self, vgname, lvname):
+        try:
+            BlockDev.lvm_lvremove(vgname, lvname, True, None)
+        except:
+            pass
+
+
+class LvmNoDevTestCase(LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        # we are checking for info log messages and default level is warning
+        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_INFO)
+
+    @classmethod
+    def tearDownClass(cls):
+        # reset back to default
+        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_WARNING)
+        BlockDev.lvm_set_global_config(None)
+
+        super(LvmNoDevTestCase, cls).tearDownClass()
+
+    def setUp(self):
+        self._log = ""
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_is_supported_pe_size(self):
+        """Verify that lvm_is_supported_pe_size works as expected"""
+
+        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024))
+        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024**2))
+        self.assertTrue(BlockDev.lvm_is_supported_pe_size(6 * 1024**2))
+        self.assertTrue(BlockDev.lvm_is_supported_pe_size(12 * 1024**2))
+        self.assertTrue(BlockDev.lvm_is_supported_pe_size(15 * 1024**2))
+        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024**3))
+
+        self.assertFalse(BlockDev.lvm_is_supported_pe_size(512))
+        self.assertFalse(BlockDev.lvm_is_supported_pe_size(4097))
+        self.assertFalse(BlockDev.lvm_is_supported_pe_size(65535))
+        self.assertFalse(BlockDev.lvm_is_supported_pe_size(32 * 1024**3))
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_get_supported_pe_sizes(self):
+        """Verify that supported PE sizes are really supported"""
+
+        supported = BlockDev.lvm_get_supported_pe_sizes()
+
+        for size in supported:
+            self.assertTrue(BlockDev.lvm_is_supported_pe_size(size))
+
+        self.assertIn(4 * 1024, supported)
+        self.assertIn(4 * 1024 **2, supported)
+        self.assertIn(16 * 1024**3, supported)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_get_max_lv_size(self):
+        """Verify that max LV size is correctly determined"""
+
+        if os.uname()[-1] == "i686":
+            # 32-bit arch
+            expected = 16 * 1024**4
+        else:
+            # 64-bit arch
+            expected = 8 * 1024**6
+
+        self.assertEqual(BlockDev.lvm_get_max_lv_size(), expected)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_round_size_to_pe(self):
+        """Verify that round_size_to_pe works as expected"""
+
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 4 * 1024**2, True), 12 * 1024**2)
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 4 * 1024**2, False), 8 * 1024**2)
+
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 6 * 1024**2, True), 12 * 1024**2)
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 6 * 1024**2, False), 6 * 1024**2)
+
+        # default PE size is 4 MiB
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 0, True), 12 * 1024**2)
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 0, False), 8 * 1024**2)
+
+        # cannot round up to GLib.MAXUINT64, but can round up over GLib.MAXUINT64 (should round down in that case)
+        biggest_multiple = (GLib.MAXUINT64 // (4 * 1024**2)) * (4 * 1024**2)
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple + (2 * 1024**2), 4 * 1024**2, True),
+                         biggest_multiple)
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple + (2 * 1024**2), 4 * 1024**2, False),
+                         biggest_multiple)
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple - (2 * 4 * 1024**2) + 1, 4 * 1024**2, True),
+                         biggest_multiple - (4 * 1024**2))
+        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple - (2 * 4 * 1024**2) + 1, 4 * 1024**2, False),
+                         biggest_multiple - (2 * 4 * 1024**2))
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_get_lv_physical_size(self):
+        """Verify that get_lv_physical_size works as expected"""
+
+        self.assertEqual(BlockDev.lvm_get_lv_physical_size(25 * 1024**3, 4 * 1024**2),
+                         25 * 1024**3)
+
+        # default PE size is 4 MiB
+        self.assertEqual(BlockDev.lvm_get_lv_physical_size(25 * 1024**3, 0),
+                         25 * 1024**3)
+
+        self.assertEqual(BlockDev.lvm_get_lv_physical_size(11 * 1024**2, 4 * 1024**2),
+                         12 * 1024**2)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_get_thpool_padding(self):
+        """Verify that get_thpool_padding works as expected"""
+
+        expected_padding = BlockDev.lvm_round_size_to_pe(int(math.ceil(11 * 1024**2 * 0.2)),
+                                                         4 * 1024**2, True)
+        self.assertEqual(BlockDev.lvm_get_thpool_padding(11 * 1024**2, 4 * 1024**2, False),
+                         expected_padding)
+
+        expected_padding = BlockDev.lvm_round_size_to_pe(int(math.ceil(11 * 1024**2 * (1.0/6.0))),
+                                                         4 * 1024**2, True)
+        self.assertEqual(BlockDev.lvm_get_thpool_padding(11 * 1024**2, 4 * 1024**2, True),
+                         expected_padding)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_get_thpool_meta_size(self):
+        """Verify that getting recommended thin pool metadata size works as expected"""
+
+
+        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 64 * 1024),
+                         1 * 1024**3)
+
+        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 128 * 1024),
+                         512 * 1024**2)
+
+        # min metadata size is 4 MiB
+        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(100 * 1024**2, 128 * 1024),
+                         4 * 1024**2)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_is_valid_thpool_md_size(self):
+        """Verify that is_valid_thpool_md_size works as expected"""
+
+        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(4 * 1024**2))
+        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(5 * 1024**2))
+        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(15 * 1024**3))
+
+        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(1 * 1024**2))
+        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(3 * 1024**2))
+        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(16 * 1024**3))
+        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(32 * 1024**3))
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_is_valid_thpool_chunk_size(self):
+        """Verify that is_valid_thpool_chunk_size works as expected"""
+
+        # 64 KiB is OK with or without discard
+        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(64 * 1024, True))
+        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(64 * 1024, False))
+
+        # 192 KiB is OK without discard, but NOK with discard
+        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(192 * 1024, False))
+        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(192 * 1024, True))
+
+        # 191 KiB is NOK in both cases
+        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(191 * 1024, False))
+        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(191 * 1024, True))
+
+    def _store_log(self, lvl, msg):
+        self._log += str((lvl, msg))
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_get_set_global_config(self):
+        """Verify that getting and setting global config works as expected"""
+
+        # setup logging
+        self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self._store_log))
+
+        # no global config set initially
+        self.assertEqual(BlockDev.lvm_get_global_config(), "")
+
+        # make sure we don't leave the config in some problematic shape
+        self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+        # set and try to get back
+        succ = BlockDev.lvm_set_global_config("bla")
+        self.assertTrue(succ)
+        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
+
+        # reset and try to get back
+        succ = BlockDev.lvm_set_global_config(None)
+        self.assertTrue(succ)
+        self.assertEqual(BlockDev.lvm_get_global_config(), "")
+
+        # set twice and try to get back twice
+        succ = BlockDev.lvm_set_global_config("foo")
+        self.assertTrue(succ)
+        succ = BlockDev.lvm_set_global_config("bla")
+        self.assertTrue(succ)
+        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
+        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
+
+        # set something sane and check it's really used
+        succ = BlockDev.lvm_set_global_config("backup {backup=0 archive=0}")
+        BlockDev.lvm_pvscan(None, False, None)
+        self.assertIn("--config", self._log)
+        self.assertIn("backup {backup=0 archive=0}", self._log)
+
+        # reset back to default
+        succ = BlockDev.lvm_set_global_config(None)
+        self.assertTrue(succ)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_get_set_global_devices_filter(self):
+        """Verify that getting and setting LVM devices filter works as expected"""
+        if not self.devices_avail:
+            self.skipTest("skipping LVM devices filter test: not supported")
+
+        # setup logging
+        self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self._store_log))
+
+        # no global config set initially
+        self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
+
+        # set and try to get back
+        succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
+        self.assertTrue(succ)
+        self.assertListEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sda"])
+
+        # reset and try to get back
+        succ = BlockDev.lvm_set_devices_filter(None)
+        self.assertTrue(succ)
+        self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
+
+        # set twice and try to get back twice
+        succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
+        self.assertTrue(succ)
+        succ = BlockDev.lvm_set_devices_filter(["/dev/sdb"])
+        self.assertTrue(succ)
+        self.assertEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sdb"])
+
+        # set something sane and check it's really used
+        succ = BlockDev.lvm_set_devices_filter(["/dev/sdb", "/dev/sdc"])
+        self.assertTrue(succ)
+        BlockDev.lvm_pvscan()
+        self.assertIn("--devices", self._log)
+        self.assertIn("/dev/sdb,/dev/sdc", self._log)
+
+        # reset back to default
+        succ = BlockDev.lvm_set_devices_filter(None)
+        self.assertTrue(succ)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_cache_get_default_md_size(self):
+        """Verify that default cache metadata size is calculated properly"""
+
+        # 1000x smaller than the data LV size, but at least 8 MiB
+        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(100 * 1024**3), (100 * 1024**3) // 1000)
+        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(80 * 1024**3), (80 * 1024**3) // 1000)
+        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(6 * 1024**3), 8 * 1024**2)
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_cache_mode_bijection(self):
+        """Verify that cache modes and their string representations map to each other"""
+
+        mode_strs = {BlockDev.LVMCacheMode.WRITETHROUGH: "writethrough",
+                     BlockDev.LVMCacheMode.WRITEBACK: "writeback",
+                     BlockDev.LVMCacheMode.UNKNOWN: "unknown",
+        }
+        for mode in mode_strs.keys():
+            self.assertEqual(BlockDev.lvm_cache_get_mode_str(mode), mode_strs[mode])
+            self.assertEqual(BlockDev.lvm_cache_get_mode_from_str(mode_strs[mode]), mode)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_cache_get_mode_from_str("bla")
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_lvm_config(self):
+        """Verify that we can correctly read from LVM config"""
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_config_get(None, "dir")
+
+        # get entire config
+        conf = BlockDev.lvm_config_get()
+        self.assertTrue(conf)
+        self.assertTrue(conf.startswith("config"))
+
+        # get just the "devices" section
+        conf = BlockDev.lvm_config_get("devices")
+        self.assertTrue(conf)
+        self.assertTrue(conf.startswith("devices"))
+
+        # let's be brave and assume devices/dir is set everywhere ti /dev
+        devdir = BlockDev.lvm_config_get("devices", "dir", "full")
+        self.assertEqual(devdir, "\"/dev\"")
+
+        devdir = BlockDev.lvm_config_get("devices", "dir", "full", values_only=False)
+        self.assertEqual(devdir, "dir=\"/dev\"")
+
+        devdir = BlockDev.lvm_config_get("devices", "dir", "default")
+        self.assertEqual(devdir, "\"/dev\"")
+
+        # let's try to override some results with --config
+        BlockDev.lvm_set_global_config("devices/dir=/test")
+
+        devdir = BlockDev.lvm_config_get("devices", "dir", "full")
+        self.assertEqual(devdir, "\"/test\"")
+
+        # "default" config should not be affected by --config
+        devdir = BlockDev.lvm_config_get("devices", "dir", "default")
+        self.assertEqual(devdir, "\"/dev\"")
+
+        # disable global config
+        devdir = BlockDev.lvm_config_get("devices", "dir", "full", global_config=False)
+        self.assertEqual(devdir, "\"/dev\"")
+
+
+class LvmPVonlyTestCase(LvmTestCase):
+    _sparse_size = 1024**3
+
+    def setUp(self):
+        self.addCleanup(self._clean_up)
+        self.dev_file = create_sparse_tempfile("lvm_test", self._sparse_size)
+        self.dev_file2 = create_sparse_tempfile("lvm_test", self._sparse_size)
+        self.dev_file3 = create_sparse_tempfile("lvm_test", self._sparse_size)
+        try:
+            self.loop_dev = create_lio_device(self.dev_file)
+        except RuntimeError as e:
+            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
+        try:
+            self.loop_dev2 = create_lio_device(self.dev_file2)
+        except RuntimeError as e:
+            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
+        try:
+            self.loop_dev3 = create_lio_device(self.dev_file3)
+        except RuntimeError as e:
+            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
+
+    def _clean_up(self):
+        for dev in (self.loop_dev, self.loop_dev2, self.loop_dev3):
+            try:
+                BlockDev.lvm_pvremove(dev)
+            except:
+                pass
+
+            try:
+                BlockDev.lvm_devices_delete(dev)
+            except:
+                pass
+
+            try:
+                delete_lio_device(dev)
+            except RuntimeError:
+                # just move on, we can do no better here
+                pass
+
+        for dfile in (self.dev_file, self.dev_file2, self.dev_file3):
+            os.unlink(dfile)
+
+
+class LvmTestPVs(LvmPVonlyTestCase):
+    @tag_test(TestTags.CORE)
+    def test_pvcreate_and_pvremove(self):
+        """Verify that it's possible to create and destroy a PV"""
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_pvcreate("/non/existing/device", 0, 0, None)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
+        self.assertTrue(succ)
+
+        # this time try to specify data_alignment and metadata_size
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 2*1024**2, 4*1024**2, None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_pvremove("/non/existing/device", None)
+
+        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
+        self.assertTrue(succ)
+
+        # already removed -- not an issue
+        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
+        self.assertTrue(succ)
+
+    def test_pvresize(self):
+        """Verify that it's possible to resize a PV"""
+
+        with self.assertRaises(GLib.GError):
+            succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None)
+
+        with self.assertRaises(GLib.GError):
+            succ = BlockDev.lvm_pvresize("/non/existing/device", 200 * 1024**2, None)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertEqual(info.pv_size, 200 * 1024**2)
+
+        succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**3, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertEqual(info.pv_size, 200 * 1024**3)
+
+    def test_pvscan(self):
+        """Verify that pvscan runs without issues with cache or without"""
+
+        succ = BlockDev.lvm_pvscan(None, False, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvscan(self.loop_dev, True, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvscan(None, True, None)
+        self.assertTrue(succ)
+
+    def test_pvinfo(self):
+        """Verify that it's possible to gather info about a PV"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertTrue(info)
+        self.assertEqual(info.pv_name, self.loop_dev)
+        self.assertTrue(info.pv_uuid)
+
+    def test_pvs(self):
+        """Verify that it's possible to gather info about PVs"""
+
+        pvs = BlockDev.lvm_pvs()
+        orig_len = len(pvs)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        pvs = BlockDev.lvm_pvs()
+        self.assertGreater(len(pvs), orig_len)
+        self.assertTrue(any(info.pv_name == self.loop_dev for info in pvs))
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertTrue(info)
+
+        self.assertTrue(any(info.pv_uuid == all_info.pv_uuid for all_info in pvs))
+
+    def test_pvremove_with_config(self):
+        """Verify that we correctly pass extra arguments when calling pvremove"""
+
+        # we add some extra arguments to pvremove (like '-ff') and we want
+        # to be sure that adding these works together with '--config'
+
+        BlockDev.lvm_set_global_config("backup {backup=0 archive=0}")
+        self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        # we are removing pv that is part of vg -- '-ff' option must be included
+        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvremove(self.loop_dev2, None)
+        self.assertTrue(succ)
+
+    @tag_test(TestTags.REGRESSION)
+    def test_set_empty_config(self):
+        succ = BlockDev.lvm_pvcreate(self.loop_dev)
+        self.assertTrue(succ)
+
+        BlockDev.lvm_set_global_config("")
+        succ = BlockDev.lvm_pvremove(self.loop_dev)
+        self.assertTrue(succ)
+
+
+class LvmPVVGTestCase(LvmPVonlyTestCase):
+    def _clean_up(self):
+        try:
+            BlockDev.lvm_vgremove("testVG", None)
+        except:
+            pass
+
+        # XXX remove lingering /dev entries
+        shutil.rmtree("/dev/testVG", ignore_errors=True)
+
+        LvmPVonlyTestCase._clean_up(self)
+
+
+class LvmTestVGs(LvmPVVGTestCase):
+    @tag_test(TestTags.CORE)
+    def test_vgcreate_vgremove(self):
+        """Verify that it is possible to create and destroy a VG"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgcreate("testVG", ["/non/existing/device"], 0, None)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        # VG already exists
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+
+        succ = BlockDev.lvm_vgremove("testVG", None)
+        self.assertTrue(succ)
+
+        # no longer exists
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgremove("testVG", None)
+
+    def test_vgrename(self):
+        """Verify that it is possible to rename a VG"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        # try rename
+        succ = BlockDev.lvm_vgrename("testVG", "testVG_new", None)
+        self.assertTrue(succ)
+
+        # rename back
+        succ = BlockDev.lvm_vgrename("testVG_new", "testVG", None)
+        self.assertTrue(succ)
+
+        # (hopefully) non-existing VG
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgrename("testVG_new", "testVG", None)
+
+    def test_vgactivate_vgdeactivate(self):
+        """Verify that it is possible to (de)activate a VG"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgactivate("nonexistingVG", None)
+
+        succ = BlockDev.lvm_vgactivate("testVG", None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgdeactivate("nonexistingVG", None)
+
+        succ = BlockDev.lvm_vgdeactivate("testVG", None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgactivate("testVG", None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgdeactivate("testVG", None)
+        self.assertTrue(succ)
+
+    def test_vgextend_vgreduce(self):
+        """Verify that it is possible to extend/reduce a VG"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgextend("nonexistingVG", self.loop_dev2, None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgextend("testVG", "/non/existing/device", None)
+
+        succ = BlockDev.lvm_vgextend("testVG", self.loop_dev2, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgreduce("testVG", self.loop_dev, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgextend("testVG", self.loop_dev, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgreduce("testVG", self.loop_dev2, None)
+        self.assertTrue(succ)
+
+        # try to remove missing PVs (there are none)
+        succ = BlockDev.lvm_vgreduce("testVG", None, None)
+        self.assertTrue(succ)
+
+    def test_vginfo(self):
+        """Verify that it is possible to gather info about a VG"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_vginfo("testVG")
+        self.assertTrue(info)
+        self.assertEqual(info.name, "testVG")
+        self.assertTrue(info.uuid)
+        self.assertEqual(info.pv_count, 2)
+        self.assertLess(info.size, 2 * 1024**3)
+        self.assertEqual(info.free, info.size)
+        self.assertEqual(info.extent_size, 4 * 1024**2)
+
+    def test_vgs(self):
+        """Verify that it's possible to gather info about VGs"""
+
+        vgs = BlockDev.lvm_vgs()
+        orig_len = len(vgs)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        vgs = BlockDev.lvm_vgs()
+        self.assertGreater(len(vgs), orig_len)
+        self.assertTrue(any(info.name == "testVG" for info in vgs))
+
+        info = BlockDev.lvm_vginfo("testVG")
+        self.assertTrue(info)
+
+        self.assertTrue(any(info.uuid == all_info.uuid for all_info in vgs))
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgremove("nonexistingVG", None)
+
+        succ = BlockDev.lvm_vgremove("testVG", None)
+        self.assertTrue(succ)
+
+        # already removed
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vgremove("testVG", None)
+
+        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
+        self.assertTrue(succ)
+
+    @tag_test(TestTags.UNSAFE)
+    def test_vglock_stop_start(self):
+        """Verify that it is possible to start and stop locking on a VG"""
+
+        # better not do anything if lvmlockd is running, shared VGs have
+        # a tendency to wreak havoc on your system if you look at them wrong
+        ret, _out, _err = run_command("systemctl is-active lvmlockd")
+        if ret == 0:
+            self.skipTest("lvmlockd is running, skipping")
+
+        _ret, out, _err = run_command("lvm config 'global/use_lvmlockd'")
+        if "use_lvmlockd=0" not in out:
+            self.skipTest("lvmlockd is enabled, skipping")
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        # this actually doesn't "test" anything, the commands will just say lvmlockd is not
+        # running and return 0, but that's good enough for us
+        succ = BlockDev.lvm_vglock_start("testVG")
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vglock_stop("testVG")
+        self.assertTrue(succ)
+
+    def test_pvtags(self):
+        """Verify that it's possible to set and get info about PV tags"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        # only pvs in a vg can be tagged so we need a vg here
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertTrue(info)
+        self.assertFalse(info.pv_tags)
+
+        succ = BlockDev.lvm_add_pv_tags(self.loop_dev, ["a", "b", "c"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertTrue(info)
+        self.assertEqual(info.pv_tags, ["a", "b", "c"])
+
+        succ = BlockDev.lvm_delete_pv_tags(self.loop_dev, ["a", "b"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertTrue(info)
+        self.assertEqual(info.pv_tags, ["c"])
+
+        succ = BlockDev.lvm_add_pv_tags(self.loop_dev, ["e"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev)
+        self.assertTrue(info)
+        self.assertEqual(info.pv_tags, ["c", "e"])
+
+    def test_vgtags(self):
+        """Verify that it's possible to set and get info about VG tags"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_vginfo("testVG")
+        self.assertTrue(info)
+        self.assertFalse(info.vg_tags)
+
+        succ = BlockDev.lvm_add_vg_tags("testVG", ["a", "b", "c"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_vginfo("testVG")
+        self.assertTrue(info)
+        self.assertEqual(info.vg_tags, ["a", "b", "c"])
+
+        succ = BlockDev.lvm_delete_vg_tags("testVG", ["a", "b"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_vginfo("testVG")
+        self.assertTrue(info)
+        self.assertEqual(info.vg_tags, ["c"])
+
+        succ = BlockDev.lvm_add_vg_tags("testVG", ["e"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_vginfo("testVG")
+        self.assertTrue(info)
+        self.assertEqual(info.vg_tags, ["c", "e"])
+
+    def test_vgcfgbackup_restore(self):
+        """Verify that it is possible to backup and restore VG configuration"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        with tempfile.TemporaryDirectory() as d:
+            succ = BlockDev.lvm_vgcfgbackup("testVG", os.path.join(d, "testVGbackup"))
+            self.assertTrue(succ)
+            self.assertTrue(os.path.isfile(os.path.join(d, "testVGbackup")))
+
+            succ = BlockDev.lvm_vgcfgrestore("testVG", os.path.join(d, "testVGbackup"))
+            self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcfgbackup("testVG", None)
+        self.assertTrue(succ)
+
+        # default location is /etc/lvm/backup/<vgname>
+        self.assertTrue(os.path.isfile("/etc/lvm/backup/testVG"))
+
+        succ = BlockDev.lvm_vgcfgrestore("testVG", None)
+        self.assertTrue(succ)
+
+        os.unlink("/etc/lvm/backup/testVG")
+
+
+class LvmPVVGLVTestCase(LvmPVVGTestCase):
+    def _clean_up(self):
+        self._lvremove("testVG", "testLV")
+        LvmPVVGTestCase._clean_up(self)
+
+
+class LvmTestLVs(LvmPVVGLVTestCase):
+    @tag_test(TestTags.CORE)
+    def test_lvcreate_lvremove(self):
+        """Verify that it's possible to create/destroy an LV"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvcreate("nonexistingVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, ["/non/existing/device"], None)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+        # no PVs specified
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+        # not enough space (only one PV)
+        with self.assertRaisesRegex(GLib.GError, "Insufficient free space"):
+            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 1048 * 1024**2, None, [self.loop_dev], None)
+
+        # enough space (two PVs)
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 1048 * 1024**2, None, [self.loop_dev, self.loop_dev2], None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvremove("nonexistingVG", "testLV", True, None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvremove("testVG", "nonexistingLV", True, None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvremove("nonexistingVG", "nonexistingLV", True, None)
+
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+        # already removed
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+
+    def test_lvremove_extra_args(self):
+        """Verify that specifying extra arguments for lvremove works as expected"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        # try multiple options together with --test, the LV should not be removed
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", False, [BlockDev.ExtraArg.new("--test", "")])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.lv_name, "testLV")
+
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, [BlockDev.ExtraArg.new("--test", "")])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.lv_name, "testLV")
+
+        # try to remove without --force
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", False, None)
+        self.assertTrue(succ)
+
+        # already removed
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+
+    def test_lvactivate_lvdeactivate(self):
+        """Verify it's possible to (de)actiavate an LV"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvactivate("nonexistingVG", "testLV", True)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvactivate("testVG", "nonexistingLV", True)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvactivate("nonexistingVG", "nonexistingLV", True)
+
+        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvdeactivate("nonexistingVG", "testLV", None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvdeactivate("testVG", "nonexistingLV", None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvdeactivate("nonexistingVG", "nonexistingLV", None)
+
+        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
+        self.assertTrue(succ)
+
+        # try activating in shared mode, unfortunately no way to check whether it really
+        # works or not
+        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, True)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
+        self.assertTrue(succ)
+
+    def test_lvresize(self):
+        """Verify that it's possible to resize an LV"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvresize("nonexistingVG", "testLV", 768 * 1024**2, None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvresize("testVG", "nonexistingLV", 768 * 1024**2, None)
+
+        # grow
+        succ = BlockDev.lvm_lvresize("testVG", "testLV", 768 * 1024**2, None)
+        self.assertTrue(succ)
+
+        # same size
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvresize("testVG", "testLV", 768 * 1024**2, None)
+
+        # shrink
+        succ = BlockDev.lvm_lvresize("testVG", "testLV", 512 * 1024**2, None)
+        self.assertTrue(succ)
+
+        # shrink, not a multiple of 512
+        succ = BlockDev.lvm_lvresize("testVG", "testLV", 500 * 1024**2, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
+        self.assertTrue(succ)
+
+        # try to shrink when deactivated
+        succ = BlockDev.lvm_lvresize("testVG", "testLV", 400 * 1024**2, None)
+        self.assertTrue(succ)
+
+    def test_lvrename(self):
+        """Verify that it's possible to rename an LV"""
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvrename("nonexistingVG", "testLV", "newTestLV", None)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvrename("testVG", "nonexistingLV", "newTestLV", None)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        # rename
+        succ = BlockDev.lvm_lvrename("testVG", "testLV", "newTestLV", None)
+        self.assertTrue(succ)
+
+        # and back
+        succ = BlockDev.lvm_lvrename("testVG", "newTestLV", "testLV", None)
+        self.assertTrue(succ)
+
+        # needs a change
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_lvrename("testVG", "testLV", "testLV", None)
+
+    @tag_test(TestTags.SLOW)
+    def test_snapshotcreate_lvorigin_snapshotmerge(self):
+        """Verify that LV snapshot support works"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvsnapshotcreate("testVG", "testLV", "testLV_bak", 256 * 1024**2, None)
+        self.assertTrue(succ)
+
+        origin_name = BlockDev.lvm_lvorigin("testVG", "testLV_bak")
+        lvi = BlockDev.lvm_lvinfo("testVG", "testLV_bak")
+        self.assertEqual(origin_name, "testLV")
+        self.assertEqual(lvi.origin, "testLV")
+        self.assertIn("snapshot", lvi.roles.split(","))
+
+        succ = BlockDev.lvm_lvsnapshotmerge("testVG", "testLV_bak", None)
+        self.assertTrue(succ)
+
+    def test_lvinfo(self):
+        """Verify that it is possible to gather info about an LV"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.lv_name, "testLV")
+        self.assertEqual(info.vg_name, "testVG")
+        self.assertTrue(info.uuid)
+        self.assertEqual(info.size, 512 * 1024**2)
+        self.assertIn("public", info.roles.split(","))
+
+    def test_lvs(self):
+        """Verify that it's possible to gather info about LVs"""
+
+        lvs = BlockDev.lvm_lvs(None)
+        orig_len = len(lvs)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        lvs = BlockDev.lvm_lvs(None)
+        self.assertGreater(len(lvs), orig_len)
+        self.assertTrue(any(info.lv_name == "testLV" and info.vg_name == "testVG" for info in lvs))
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+
+        self.assertTrue(any(info.uuid == all_info.uuid for all_info in lvs))
+
+        lvs = BlockDev.lvm_lvs("testVG")
+        self.assertEqual(len(lvs), 1)
+
+    def test_lvinfo_tree(self):
+        """Verify that it's possible to gather info about LVs"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 10 * 1024**2)
+        self.assertTrue(succ)
+
+        lvs = BlockDev.lvm_lvs("testVG")
+        self.assertEqual(len(lvs), 1)
+        self.assertListEqual([lv.lv_name for lv in lvs], ["testLV"])
+
+        # the LV will have a single segment on loop_dev
+        info = BlockDev.lvm_lvinfo_tree("testVG", "testLV")
+        self.assertEqual(info.segtype, "linear")
+        self.assertEqual(len(info.segs), 1)
+        self.assertEqual(info.segs[0].pvdev, self.loop_dev)
+
+        # add second LV
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV2", 10 * 1024**2)
+        self.assertTrue(succ)
+        self.addCleanup(self._lvremove, "testVG", "testLV2")
+
+        lvs = BlockDev.lvm_lvs("testVG")
+        self.assertEqual(len(lvs), 2)
+        self.assertCountEqual([lv.lv_name for lv in lvs], ["testLV", "testLV2"])
+
+        # by resizing the first LV we will create two segments
+        succ = BlockDev.lvm_lvresize("testVG", "testLV", 20 * 1024**2, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo_tree("testVG", "testLV")
+        self.assertEqual(info.segtype, "linear")
+        self.assertEqual(len(info.segs), 2)
+        self.assertEqual(info.segs[0].pvdev, self.loop_dev)
+        self.assertEqual(info.segs[1].pvdev, self.loop_dev)
+        self.assertNotEqual(info.segs[0].pv_start_pe, info.segs[1].pv_start_pe)
+
+        lvs = BlockDev.lvm_lvs("testVG")
+        self.assertEqual(len(lvs), 2)
+        self.assertListEqual([lv.lv_name for lv in lvs], ["testLV", "testLV2"])
+
+    @tag_test(TestTags.SLOW)
+    def test_create_cached_lv(self):
+        """Verify that it is possible to create a cached LV in a single step"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_create_cached_lv("testVG", "testLV", 512 * 1024**2, 256 * 1024**2, 10 * 1024**2,
+                                                   BlockDev.LVMCacheMode.WRITEBACK, 0,
+                                                   [self.loop_dev], [self.loop_dev2])
+        self.assertTrue(succ)
+
+    @tag_test(TestTags.SLOW)
+    def test_cache_get_pool_name(self):
+        """Verify that it is possible to get the name of the cache pool"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
+        self.assertTrue(succ)
+
+        if LVM_VERSION < Version("2.03.06"):
+            cpool_name = "testCache"
+        else:
+            # since 2.03.06 LVM adds _cpool suffix to the cache pool after attaching it
+            cpool_name = "testCache_cpool"
+
+        self.assertEqual(BlockDev.lvm_cache_pool_name("testVG", "testLV"), cpool_name)
+
+    @tag_test(TestTags.SLOW)
+    def test_create_writecached_lv(self):
+        """Verify that it is possible to create a cached LV in a single step"""
+
+        if LVM_VERSION < Version("2.03.10") and self.test_type == "dbus":
+            self.skipTest("LVM writecache support in DBus API not available")
+
+        lvm_segtypes = self._get_lvm_segtypes()
+        if "writecache" not in lvm_segtypes:
+            self.skipTest("LVM writecache support not available")
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_writecache_create_cached_lv("testVG", "testLV", 512 * 1024**2, 256 * 1024**2,
+                                                        [self.loop_dev], [self.loop_dev2])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertIsNotNone(info)
+        self.assertEqual(info.segtype, "writecache")
+
+    @tag_test(TestTags.SLOW)
+    def test_cache_get_stats(self):
+        """Verify that it is possible to get stats for a cached LV"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
+        self.assertTrue(succ)
+
+        stats = BlockDev.lvm_cache_stats("testVG", "testLV")
+        self.assertTrue(stats)
+        self.assertEqual(stats.cache_size, 512 * 1024**2)
+        self.assertEqual(stats.md_size, 8 * 1024**2)
+        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
+
+    @tag_test(TestTags.SLOW)
+    def test_thinpool_cache_get_stats(self):
+        """Verify that it is possible to get stats for a cached thinpool"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_attach("testVG", "testPool", "testCache", None)
+        self.assertTrue(succ)
+
+        # just ask for the pool itself even if it's not technically cached
+        stats = BlockDev.lvm_cache_stats("testVG", "testPool")
+        self.assertTrue(stats)
+        self.assertEqual(stats.cache_size, 512 * 1024**2)
+        self.assertEqual(stats.md_size, 8 * 1024**2)
+        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
+
+        # same should work when explicitly asking for the data LV
+        stats = BlockDev.lvm_cache_stats("testVG", "testPool_tdata")
+        self.assertTrue(stats)
+        self.assertEqual(stats.cache_size, 512 * 1024**2)
+        self.assertEqual(stats.md_size, 8 * 1024**2)
+        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
+
+    def test_vgtags(self):
+        """Verify that it's possible to set and get info about LV tags"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertFalse(info.lv_tags)
+
+        succ = BlockDev.lvm_add_lv_tags("testVG", "testLV", ["a", "b", "c"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.lv_tags, ["a", "b", "c"])
+
+        succ = BlockDev.lvm_delete_lv_tags("testVG", "testLV", ["a", "b"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.lv_tags, ["c"])
+
+        succ = BlockDev.lvm_add_lv_tags("testVG", "testLV", ["e"])
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.lv_tags, ["c", "e"])
+
+
+class LvmTestLVcreateType(LvmPVVGLVTestCase):
+
+    _sparse_size = 200 * 1024**2
+
+    def test_lvcreate_type(self):
+        """Verify it's possible to create LVs with various types"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        # try to create a striped LV
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "striped", [self.loop_dev, self.loop_dev2], None)
+        self.assertTrue(succ)
+
+        # verify that the LV has the requested segtype
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertEqual(info.segtype, "striped")
+
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+        with wait_for_sync("testVG", "testLV"):
+            # try to create a mirrored LV
+            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "mirror", [self.loop_dev, self.loop_dev2], None)
+            self.assertTrue(succ)
+
+        # verify that the LV has the requested segtype
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertEqual(info.segtype, "mirror")
+
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+        with wait_for_sync("testVG", "testLV"):
+            # try to create a raid1 LV
+            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "raid1", [self.loop_dev, self.loop_dev2], None)
+            self.assertTrue(succ)
+
+        # verify that the LV has the requested segtype
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertEqual(info.segtype, "raid1")
+
+        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+
+class LvmTestPartialLVs(LvmPVVGLVTestCase):
+    # the mirror halves are actually written to during sync-up and the
+    # default sparse_size of 1Gig is too much for a regular /tmp, so
+    # let's use smaller ones here.
+    #
+    _sparse_size = 20*1024**2
+
+    @tag_test(TestTags.CORE)
+    def test_lvpartial(self):
+        """Verify that missing PVs are detected and can be dealt with"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev3, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2, self.loop_dev3], 0, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_pvinfo(self.loop_dev2)
+        self.assertTrue(info)
+        self.assertFalse(info.missing)
+        self.assertEqual(info.vg_name, "testVG")
+        loop_dev2_pv_uuid = info.pv_uuid
+
+        # Create a mirrored LV on the first two PVs
+        with wait_for_sync("testVG", "testLV"):
+            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 5 * 1024**2, "raid1",
+                                         [self.loop_dev, self.loop_dev2], None)
+            self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.attr[8], "-")
+
+        # Check that lvs_tree returns the expected structure
+
+        def assert_lv_subs(info, segtype, len_segs, len_data, len_metadata):
+            self.assertTrue(info)
+            self.assertEqual(info.segtype, segtype)
+            self.assertEqual(len(info.segs), len_segs)
+            self.assertEqual(len(info.data_lvs), len_data)
+            self.assertEqual(len(info.metadata_lvs), len_metadata)
+
+        def assert_lv_single_pv(info, pv):
+            if pv:
+                assert_lv_subs(info, "linear", 1, 0, 0)
+                self.assertEqual(info.segs[0].pvdev, pv)
+            else:
+                assert_lv_subs(info, "linear", 0, 0, 0)
+
+        def assert_raid1_structure(pv1, pv2):
+            lvs = { lv.lv_name: lv for lv in BlockDev.lvm_lvs_tree("testVG") }
+            info = lvs["testLV"]
+            assert_lv_subs(info, "raid1", 0, 2, 2)
+            assert_lv_single_pv(lvs["["+info.data_lvs[0]+"]"], pv1)
+            assert_lv_single_pv(lvs["["+info.data_lvs[1]+"]"], pv2)
+            assert_lv_single_pv(lvs["["+info.metadata_lvs[0]+"]"], pv1)
+            assert_lv_single_pv(lvs["["+info.metadata_lvs[1]+"]"], pv2)
+
+        assert_raid1_structure(self.loop_dev, self.loop_dev2)
+
+        # Disconnect the second PV, this should cause it to be flagged
+        # as missing, and testLV to be reported as "partial".
+        delete_lio_device(self.loop_dev2)
+
+        if self.test_type == "dbus":
+            # Kick lvmdbusd so that it notices the missing PV.
+            dbus.SystemBus().call_blocking('com.redhat.lvmdbus1', '/com/redhat/lvmdbus1/Manager',
+                                        'com.redhat.lvmdbus1.Manager', 'Refresh', '', [])
+
+        pvs = BlockDev.lvm_pvs()
+        found = False
+        for pv in pvs:
+            if pv.pv_uuid == loop_dev2_pv_uuid:
+                found = True
+                self.assertTrue(pv.missing)
+                self.assertEqual(pv.vg_name, "testVG")
+        self.assertTrue(found)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertTrue(info)
+        self.assertEqual(info.attr[8], "p")
+
+        # lvs_tree should report the second stripe to be missing
+        assert_raid1_structure(self.loop_dev, None)
+
+        # remove records of missing PVs
+        succ = BlockDev.lvm_vgreduce("testVG", None, None)
+        self.assertTrue(succ)
+
+        pvs = BlockDev.lvm_pvs()
+        found = False
+        for pv in pvs:
+            if pv.pv_uuid == loop_dev2_pv_uuid:
+                found = True
+        self.assertFalse(found)
+
+        # lvs_tree should still report the second stripe to be missing
+        assert_raid1_structure(self.loop_dev, None)
+
+        # repair testLV with the third PV
+        with wait_for_sync("testVG", "testLV"):
+            succ = BlockDev.lvm_lvrepair("testVG", "testLV", [self.loop_dev3])
+            self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertEqual(info.attr[8], "-")
+
+        assert_raid1_structure(self.loop_dev, self.loop_dev3)
+
+
+class LvmPVVGthpoolTestCase(LvmPVVGTestCase):
+    def _clean_up(self):
+        try:
+            BlockDev.lvm_lvremove("testVG", "testPool", True, None)
+        except:
+            pass
+
+        LvmPVVGTestCase._clean_up(self)
+
+
+class LvmTestThpool(LvmPVVGthpoolTestCase):
+    def test_lvs_all(self):
+        """Verify that info is gathered for all LVs"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
+        self.assertTrue(succ)
+
+        # there should be at least 3 LVs -- testPool, [testPool_tdata], [testPool_tmeta] (plus probably some spare LVs)
+        lvs = BlockDev.lvm_lvs("testVG")
+        self.assertGreater(len(lvs), 3)
+
+    @tag_test(TestTags.CORE)
+    def test_thpoolcreate(self):
+        """Verify that it is possible to create a thin pool"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testPool")
+        self.assertIn("t", info.attr)
+        self.assertIn("private", info.roles.split(","))
+
+    def test_thpool_convert(self):
+        """Verify that it is possible to create a thin pool by conversion"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        # the name of the data LV is used for the pool
+        succ = BlockDev.lvm_lvcreate("testVG", "dataLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+        succ = BlockDev.lvm_lvcreate("testVG", "metadataLV", 50 * 1024**2, None, [self.loop_dev2], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thpool_convert("testVG", "dataLV", "metadataLV", "testPool", None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testPool")
+        self.assertIn("t", info.attr)
+
+    def test_data_metadata_lv_name(self):
+        """Verify that it is possible to get name of the data/metadata LV"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
+        self.assertTrue(succ)
+
+        lvi = BlockDev.lvm_lvinfo("testVG", "testPool")
+        self.assertTrue(lvi.data_lv)
+        self.assertTrue(lvi.data_lv.startswith("testPool"))
+        self.assertIn("_tdata", lvi.data_lv)
+
+        info = BlockDev.lvm_lvinfo("testVG", lvi.data_lv)
+        self.assertTrue(info.attr.startswith("T"))
+        self.assertIn("private", info.roles.split(","))
+        self.assertIn("data", info.roles.split(","))
+
+        lvi = BlockDev.lvm_lvinfo("testVG", "testPool")
+        self.assertTrue(lvi.metadata_lv)
+        self.assertTrue(lvi.metadata_lv.startswith("testPool"))
+        self.assertIn("_tmeta", lvi.metadata_lv)
+
+        info = BlockDev.lvm_lvinfo("testVG", lvi.metadata_lv)
+        self.assertTrue(info.attr.startswith("e"))
+        self.assertIn("private", info.roles.split(","))
+        self.assertIn("metadata", info.roles.split(","))
+
+
+class LvmPVVGLVthLVTestCase(LvmPVVGthpoolTestCase):
+    def _clean_up(self):
+        self._lvremove("testVG", "testThLV")
+
+        LvmPVVGthpoolTestCase._clean_up(self)
+
+
+class LvmTestThLV(LvmPVVGLVthLVTestCase):
+    @tag_test(TestTags.CORE)
+    def test_thlvcreate_thpoolname(self):
+        """Verify that it is possible to create a thin LV and get its pool name"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, None, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thlvcreate("testVG", "testPool", "testThLV", 1024**3, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testPool")
+        self.assertIn("t", info.attr)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testThLV")
+        self.assertIn("V", info.attr)
+
+        pool = BlockDev.lvm_thlvpoolname("testVG", "testThLV")
+        lvi = BlockDev.lvm_lvinfo("testVG", "testThLV")
+        self.assertEqual(pool, "testPool")
+        self.assertEqual(lvi.pool_lv, "testPool")
+
+    def test_thsnapshotcreate(self):
+        """Verify that it is possible to create a thin LV snapshot"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, None, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_thlvcreate("testVG", "testPool", "testThLV", 1024**3, None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testPool")
+        self.assertIn("t", info.attr)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testThLV")
+        self.assertIn("V", info.attr)
+
+        succ = BlockDev.lvm_thsnapshotcreate("testVG", "testThLV", "testThLV_bak", "testPool", None)
+        self.assertTrue(succ)
+        self.addCleanup(self._lvremove, "testVG", "testThLV_bak")
+
+        info = BlockDev.lvm_lvinfo("testVG", "testThLV_bak")
+        self.assertIn("V", info.attr)
+        self.assertIn("snapshot", info.roles.split(","))
+        self.assertIn("thinsnapshot", info.roles.split(","))
+
+
+
+class LvmPVVGLVcachePoolTestCase(LvmPVVGLVTestCase):
+    def _clean_up(self):
+        self._lvremove("testVG", "testCache")
+        # lets help udev with removing stale symlinks
+        try:
+            if not BlockDev.lvm_lvs("testVG") and os.path.exists("/dev/testVG/testCache_meta"):
+                shutil.rmtree("/dev/testVG", ignore_errors=True)
+        except:
+            pass
+
+        LvmPVVGLVTestCase._clean_up(self)
+
+
+class LvmTestCache(LvmPVVGLVcachePoolTestCase):
+    @tag_test(TestTags.SLOW, TestTags.UNSTABLE)
+    def test_cache_pool_create_remove(self):
+        """Verify that is it possible to create and remove a cache pool"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev])
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvremove("testVG", "testCache", True, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITEBACK,
+                                              BlockDev.LVMCachePoolFlags.STRIPED|BlockDev.LVMCachePoolFlags.META_RAID1,
+                                              [self.loop_dev, self.loop_dev2])
+        self.assertTrue(succ)
+
+    @tag_test(TestTags.SLOW)
+    def test_cache_pool_convert(self):
+        """Verify that it is possible to create a cache pool by conversion"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "dataLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+        succ = BlockDev.lvm_lvcreate("testVG", "metadataLV", 50 * 1024**2, None, [self.loop_dev2], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_pool_convert("testVG", "dataLV", "metadataLV", "testCache", None)
+        self.assertTrue(succ)
+
+    @tag_test(TestTags.SLOW)
+    def test_cache_pool_attach_detach(self):
+        """Verify that is it possible to attach and detach a cache pool"""
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
+        self.assertTrue(succ)
+
+        # detach and destroy (the last arg)
+        succ = BlockDev.lvm_cache_detach("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+        # once more and do not destroy this time
+        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_cache_detach("testVG", "testLV", False, None)
+        self.assertTrue(succ)
+
+        lvs = BlockDev.lvm_lvs("testVG")
+        self.assertTrue(any(info.lv_name == "testCache" for info in lvs))
+
+    @tag_test(TestTags.SLOW)
+    def test_writecache_attach_detach(self):
+        """Verify that is it possible to attach and detach a writecache LV"""
+
+        if LVM_VERSION < Version("2.03.10") and self.test_type == "dbus":
+            self.skipTest("LVM writecache support in DBus API not available")
+
+        lvm_segtypes = self._get_lvm_segtypes()
+        if "writecache" not in lvm_segtypes:
+            self.skipTest("LVM writecache support not available")
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testCache", 512 * 1024**2, None, [self.loop_dev2], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_writecache_attach("testVG", "testLV", "testCache", None)
+        self.assertTrue(succ)
+
+        info = BlockDev.lvm_lvinfo("testVG", "testLV")
+        self.assertIsNotNone(info)
+        self.assertEqual(info.segtype, "writecache")
+
+        # detach and destroy (the last arg)
+        succ = BlockDev.lvm_writecache_detach("testVG", "testLV", True, None)
+        self.assertTrue(succ)
+
+        # once more and do not destroy this time
+        succ = BlockDev.lvm_lvcreate("testVG", "testCache", 512 * 1024**2, None, [self.loop_dev2], None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_writecache_attach("testVG", "testLV", "testCache", None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_writecache_detach("testVG", "testLV", False, None)
+        self.assertTrue(succ)
+
+        lvs = BlockDev.lvm_lvs("testVG")
+        self.assertTrue(any(info.lv_name == "testCache" for info in lvs))
+
+class LvmVDOTest(LvmTestCase):
+
+    loop_size = 8 * 1024**3
+
+    @classmethod
+    def setUpClass(cls):
+        if not BlockDev.utils_have_kernel_module("dm-vdo"):
+            raise unittest.SkipTest("VDO kernel module not available, skipping.")
+
+        try:
+            BlockDev.utils_load_kernel_module("dm-vdo")
+        except GLib.GError as e:
+            if "File exists" not in e.message:
+                raise unittest.SkipTest("cannot load VDO kernel module, skipping.")
+
+        if LVM_VERSION < Version("2.3.07"):
+            raise unittest.SkipTest("LVM version 2.3.07 or newer needed for LVM VDO.")
+
+        if not shutil.which("vdoformat"):
+            raise unittest.SkipTest("vdoformat executable not found in $PATH, skipping.")
+
+        super().setUpClass()
+
+    def setUp(self):
+        self.addCleanup(self._clean_up)
+        self.dev_file = create_sparse_tempfile("vdo_test", self.loop_size)
+        try:
+            self.loop_dev = create_lio_device(self.dev_file)
+        except RuntimeError as e:
+            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vgcreate("testVDOVG", [self.loop_dev], 0, None)
+        self.assertTrue(succ)
+
+    def _clean_up(self):
+        try:
+            BlockDev.lvm_lvremove("testVDOVG", "vdoPool", True, None)
+        except:
+            pass
+
+        BlockDev.lvm_vgremove("testVDOVG")
+        BlockDev.lvm_pvremove(self.loop_dev)
+
+        # XXX remove lingering /dev entries
+        shutil.rmtree("/dev/testVDOVG", ignore_errors=True)
+
+        try:
+            delete_lio_device(self.loop_dev)
+        except RuntimeError:
+            # just move on, we can do no better here
+            pass
+        os.unlink(self.dev_file)
+
+    @tag_test(TestTags.SLOW, TestTags.CORE)
+    def test_vdo_pool_create(self):
+        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
+        self.assertTrue(succ)
+
+        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
+        self.assertIsNotNone(lv_info)
+        self.assertEqual(lv_info.segtype, "vdo")
+        self.assertEqual(lv_info.pool_lv, "vdoPool")
+
+        pool_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool")
+        self.assertEqual(pool_info.segtype, "vdo-pool")
+        self.assertEqual(pool_info.data_lv, "vdoPool_vdata")
+        if LVM_VERSION >= Version("2.03.24"):
+            self.assertGreater(pool_info.data_percent, 0)
+
+        pool = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV")
+        self.assertEqual(pool, lv_info.pool_lv)
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertEqual(vdo_info.operating_mode, BlockDev.LVMVDOOperatingMode.NORMAL)
+        self.assertEqual(vdo_info.compression_state, BlockDev.LVMVDOCompressionState.ONLINE)
+        self.assertTrue(vdo_info.compression)
+        self.assertTrue(vdo_info.deduplication)
+        self.assertGreater(vdo_info.index_memory_size, 0)
+        self.assertGreater(vdo_info.used_size, 0)
+        self.assertTrue(0 <= vdo_info.saving_percent <= 100)
+
+        lvs = BlockDev.lvm_lvs("testVDOVG")
+        self.assertIn("vdoPool", [l.lv_name for l in lvs])
+        self.assertIn("vdoLV", [l.lv_name for l in lvs])
+
+        mode_str = BlockDev.lvm_get_vdo_operating_mode_str(vdo_info.operating_mode)
+        self.assertEqual(mode_str, "normal")
+
+        state_str = BlockDev.lvm_get_vdo_compression_state_str(vdo_info.compression_state)
+        self.assertEqual(state_str, "online")
+
+        policy_str = BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy)
+        self.assertIn(policy_str, ["sync", "async", "auto"])
+
+        state_str = BlockDev.lvm_get_vdo_compression_state_str(vdo_info.compression_state)
+        self.assertEqual(state_str, "online")
+
+    @tag_test(TestTags.SLOW)
+    def test_vdo_pool_create_options(self):
+        # set index size to 300 MiB, disable compression and write policy to sync
+        policy = BlockDev.lvm_get_vdo_write_policy_from_str("sync")
+        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3,
+                                            300 * 1024**2, False, True, policy)
+        self.assertTrue(succ)
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertEqual(vdo_info.index_memory_size, 300 * 1024**2)
+        self.assertFalse(vdo_info.compression)
+        self.assertTrue(vdo_info.deduplication)
+        self.assertEqual(BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy), "sync")
+
+    @tag_test(TestTags.SLOW)
+    def test_vdo_pool_create_noname(self):
+        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", None, 7 * 1024**3, 35 * 1024**3)
+        self.assertTrue(succ)
+
+        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
+        self.assertIsNotNone(lv_info)
+        self.assertEqual(lv_info.segtype, "vdo")
+
+        pool_name = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV")
+        self.assertEqual(lv_info.pool_lv, pool_name)
+        pool_info = BlockDev.lvm_lvinfo("testVDOVG", pool_name)
+        self.assertEqual(pool_info.segtype, "vdo-pool")
+
+    @tag_test(TestTags.SLOW)
+    def test_resize(self):
+        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 5 * 1024**3, 10 * 1024**3)
+        self.assertTrue(succ)
+
+        # "physical" resize first (pool), shrinking not allowed
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_vdo_pool_resize("testVDOVG", "vdoPool", 4 * 1024**3)
+
+        succ = BlockDev.lvm_vdo_pool_resize("testVDOVG", "vdoPool", 7 * 1024**3)
+        self.assertTrue(succ)
+        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool")
+        self.assertEqual(lv_info.size, 7 * 1024**3)
+
+        # "logical" resize (LV)
+        succ = BlockDev.lvm_vdo_resize("testVDOVG", "vdoLV", 35 * 1024**3)
+        self.assertTrue(succ)
+        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
+        self.assertEqual(lv_info.size, 35 * 1024**3)
+
+    @tag_test(TestTags.SLOW)
+    def test_enable_disable_compression(self):
+        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3,
+                                            300 * 1024**2)
+        self.assertTrue(succ)
+
+        # enabled by default
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertTrue(vdo_info.compression)
+
+        # disable compression
+        succ = BlockDev.lvm_vdo_disable_compression("testVDOVG", "vdoPool")
+        self.assertTrue(succ)
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertFalse(vdo_info.compression)
+
+        # and enable compression back
+        succ = BlockDev.lvm_vdo_enable_compression("testVDOVG", "vdoPool")
+        self.assertTrue(succ)
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertTrue(vdo_info.compression)
+
+    @tag_test(TestTags.SLOW)
+    def test_enable_disable_deduplication(self):
+        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3,
+                                            300 * 1024**2)
+        self.assertTrue(succ)
+
+        # enabled by default
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertTrue(vdo_info.deduplication)
+
+        # disable compression
+        succ = BlockDev.lvm_vdo_disable_deduplication("testVDOVG", "vdoPool")
+        self.assertTrue(succ)
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertFalse(vdo_info.deduplication)
+
+        # and enable compression back
+        succ = BlockDev.lvm_vdo_enable_deduplication("testVDOVG", "vdoPool")
+        self.assertTrue(succ)
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertTrue(vdo_info.deduplication)
+
+    @tag_test(TestTags.SLOW)
+    def test_vdo_pool_convert(self):
+        succ = BlockDev.lvm_lvcreate("testVDOVG", "testLV", 7 * 1024**3)
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_vdo_pool_convert("testVDOVG", "testLV", "vdoLV", 35 * 1024**3)
+        self.assertTrue(succ)
+
+        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
+        self.assertIsNotNone(lv_info)
+        self.assertEqual(lv_info.size, 35 * 1024**3)
+        self.assertEqual(lv_info.segtype, "vdo")
+        self.assertEqual(lv_info.pool_lv, "testLV")
+
+        pool_info = BlockDev.lvm_lvinfo("testVDOVG", "testLV")
+        self.assertIsNotNone(pool_info)
+        self.assertEqual(pool_info.segtype, "vdo-pool")
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "testLV")
+        self.assertIsNotNone(vdo_info)
+        self.assertTrue(vdo_info.compression)
+        self.assertTrue(vdo_info.deduplication)
+        self.assertEqual(BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy), "auto")
+
+    @tag_test(TestTags.SLOW)
+    def test_stats(self):
+        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
+        self.assertTrue(succ)
+
+        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
+        self.assertIsNotNone(vdo_info)
+        self.assertTrue(vdo_info.deduplication)
+
+        vdo_stats = BlockDev.lvm_vdo_get_stats("testVDOVG", "vdoPool")
+
+        # just sanity check
+        self.assertNotEqual(vdo_stats.saving_percent, -1)
+        self.assertNotEqual(vdo_stats.used_percent, -1)
+        self.assertNotEqual(vdo_stats.block_size, -1)
+        self.assertNotEqual(vdo_stats.logical_block_size, -1)
+        self.assertNotEqual(vdo_stats.physical_blocks, -1)
+        self.assertNotEqual(vdo_stats.data_blocks_used, -1)
+        self.assertNotEqual(vdo_stats.overhead_blocks_used, -1)
+        self.assertNotEqual(vdo_stats.logical_blocks_used, -1)
+        self.assertNotEqual(vdo_stats.write_amplification_ratio, -1)
+
+        full_stats = BlockDev.lvm_vdo_get_stats_full("testVDOVG", "vdoPool")
+        self.assertIn("writeAmplificationRatio", full_stats.keys())
+
+
+class LvmTestDevicesFile(LvmPVonlyTestCase):
+    devicefile = "bd_lvm_tests.devices"
+
+    @classmethod
+    def tearDownClass(cls):
+        try:
+            os.remove("/etc/lvm/devices/" + cls.devicefile)
+        except FileNotFoundError:
+            pass
+
+        super(LvmTestDevicesFile, cls).tearDownClass()
+
+    def test_devices_add_delete(self):
+        if not self.devices_avail:
+            self.skipTest("skipping LVM devices file test: not supported")
+
+        self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+        # force-enable the feature, it might be disabled by default
+        succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=1 }")
+        self.assertTrue(succ)
+
+        succ = BlockDev.lvm_pvcreate(self.loop_dev)
+        self.assertTrue(succ)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_devices_add("/non/existing/device", self.devicefile)
+
+        with self.assertRaises(GLib.GError):
+            BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
+
+        succ = BlockDev.lvm_devices_add(self.loop_dev, self.devicefile)
+        self.assertTrue(succ)
+
+        dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+        self.assertIn(self.loop_dev, dfile)
+
+        succ = BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
+        self.assertTrue(succ)
+
+        dfile = read_file("/etc/lvm/devices/" + self.devicefile)
+        self.assertNotIn(self.loop_dev, dfile)
+
+        BlockDev.lvm_set_global_config(None)
+
+    def test_devices_enabled(self):
+        if not self.devices_avail:
+            self.skipTest("skipping LVM devices file test: not supported")
+
+        self.addCleanup(BlockDev.lvm_set_global_config, None)
+
+        # checking if the feature is enabled or disabled is hard so lets just disable
+        # the devices file using the global config and check lvm_devices_add fails
+        # with the correct exception message
+        succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=0 }")
+        self.assertTrue(succ)
+
+        with self.assertRaisesRegex(GLib.GError, "LVM devices file not enabled."):
+            BlockDev.lvm_devices_add("", self.devicefile)
diff -pruN 3.3.1-3/tests/btrfs_test.py 3.4.0-1/tests/btrfs_test.py
--- 3.3.1-3/tests/btrfs_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/btrfs_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -43,7 +43,7 @@ class BtrfsTest(unittest.TestCase):
 class BtrfsPluginVersionCase(BtrfsTest):
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.BTRFS), "libbd_btrfs.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.BTRFS), "libbd_btrfs.so.3")
 
 class BtrfsTestCase(BtrfsTest):
 
@@ -358,6 +358,13 @@ class BtrfsTestListSubvolumes(BtrfsTestC
         self.assertEqual(subvols[0].path, "subvol1")
         self.assertEqual(subvols[1].path, "subvol1/bar")
 
+        # test also subvolumes with spaces in name
+        succ = BlockDev.btrfs_create_subvolume(TEST_MNT, "subvol with spaces", None)
+        self.assertTrue(succ)
+
+        subvols = BlockDev.btrfs_list_subvolumes(TEST_MNT, False)
+        self.assertTrue(any(subvol.path == "subvol with spaces" for subvol in subvols))
+
     @tag_test(TestTags.CORE)
     def test_list_subvolumes_different_mount(self):
         """Verify that it is possible get to info about subvolumes with subvol= mount option"""
diff -pruN 3.3.1-3/tests/crypto_test.py 3.4.0-1/tests/crypto_test.py
--- 3.3.1-3/tests/crypto_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/crypto_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -42,6 +42,9 @@ class CryptoTestCase(unittest.TestCase):
 
     _dm_name = "libblockdevTestLUKS"
     _sparse_size = 1024**3
+    _num_devices = 1
+    loop_devs = []
+    dev_files = []
 
     @classmethod
     def setUpClass(cls):
@@ -55,16 +58,16 @@ class CryptoTestCase(unittest.TestCase):
 
     def setUp(self):
         self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("crypto_test", self._sparse_size)
-        self.dev_file2 = create_sparse_tempfile("crypto_test2", self._sparse_size)
-        try:
-            self.loop_dev = create_lio_device(self.dev_file)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev2 = create_lio_device(self.dev_file2)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
+
+        for i in range(self._num_devices):
+            dev_file = create_sparse_tempfile("crypto_test", self._sparse_size)
+            self.dev_files.append(dev_file)
+
+            try:
+                loop_dev = create_lio_device(self.dev_files[i])
+                self.loop_devs.append(loop_dev)
+            except RuntimeError as e:
+                raise RuntimeError("Failed to setup loop device for testing: %s" % e)
 
         # make a key file
         handle, self.keyfile = tempfile.mkstemp(prefix="libblockdev_test_keyfile", text=False)
@@ -77,31 +80,42 @@ class CryptoTestCase(unittest.TestCase):
         except:
             pass
 
-        try:
-            delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
-
-        try:
-            delete_lio_device(self.loop_dev2)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file2)
+        for i in range(self._num_devices):
+            try:
+                delete_lio_device(self.loop_devs[i])
+            except RuntimeError:
+                # just move on, we can do no better here
+                pass
+            os.unlink(self.dev_files[i])
 
         os.unlink(self.keyfile)
 
-    def _luks_format(self, device, passphrase, keyfile=None, luks_version=BlockDev.CryptoLUKSVersion.LUKS1):
+        self.dev_files.clear()
+        self.loop_devs.clear()
+
+    def _luks_format(self, device, passphrase, keyfile=None, luks_version=BlockDev.CryptoLUKSVersion.LUKS1, fast_pbkdf=False):
+        if fast_pbkdf:
+            pbkdf = BlockDev.CryptoLUKSPBKDF(type="pbkdf2", iterations=1000)
+            extra = BlockDev.CryptoLUKSExtra(pbkdf=pbkdf)
+        else:
+            extra = None
         ctx = BlockDev.CryptoKeyslotContext(passphrase=passphrase)
-        BlockDev.crypto_luks_format(device, context=ctx, luks_version=luks_version)
+        BlockDev.crypto_luks_format(device, context=ctx, luks_version=luks_version, extra=extra)
         if keyfile:
             nctx = BlockDev.CryptoKeyslotContext(keyfile=keyfile)
             BlockDev.crypto_luks_add_key(device, ctx, nctx)
 
-    def _luks2_format(self, device, passphrase, keyfile=None):
-        return self._luks_format(device, passphrase, keyfile, BlockDev.CryptoLUKSVersion.LUKS2)
+    def _luks2_format(self, device, passphrase, keyfile=None, fast_pbkdf=False):
+        return self._luks_format(device, passphrase, keyfile, BlockDev.CryptoLUKSVersion.LUKS2, fast_pbkdf=fast_pbkdf)
+
+    def _is_fips_enabled(self):
+        if not os.path.exists("/proc/sys/crypto/fips_enabled"):
+            # if the file doesn't exist, we are definitely not in FIPS mode
+            return False
+
+        with open("/proc/sys/crypto/fips_enabled", "r") as f:
+            enabled = f.read()
+        return enabled.strip() == "1"
 
 class CryptoNoDevTestCase(CryptoTestCase):
     def setUp(self):
@@ -110,7 +124,7 @@ class CryptoNoDevTestCase(CryptoTestCase
 
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.CRYPTO), "libbd_crypto.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.CRYPTO), "libbd_crypto.so.3")
 
     @tag_test(TestTags.NOSTORAGE)
     def test_generate_backup_passhprase(self):
@@ -155,15 +169,22 @@ class CryptoTestFormat(CryptoTestCase):
 
         # the simple case with password
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(device=self.loop_dev, cipher="aes-xts-plain64",
+        succ = BlockDev.crypto_luks_format(device=self.loop_devs[0], cipher="aes-xts-plain64",
                                            key_size=0, context=ctx, min_entropy=0,
                                            luks_version=BlockDev.CryptoLUKSVersion.LUKS1,
                                            extra=None)
         self.assertTrue(succ)
 
+        # just make sure default pbkdf iterations are used with extra=None
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
+        m = re.search(r"MK iterations:\s*(\d+)\s*", out)
+        if not m or len(m.groups()) != 1:
+            self.fail("Failed to get pbkdf information from:\n%s %s" % (out, err))
+        self.assertGreater(int(m.group(1)), 1000)
+
         # create with a keyfile
         ctx = BlockDev.CryptoKeyslotContext(keyfile=self.keyfile, keyfile_offset=0, key_size=0)
-        succ = BlockDev.crypto_luks_format(device=self.loop_dev, cipher="aes-xts-plain64",
+        succ = BlockDev.crypto_luks_format(device=self.loop_devs[0], cipher="aes-xts-plain64",
                                            key_size=0, context=ctx, min_entropy=0,
                                            luks_version=BlockDev.CryptoLUKSVersion.LUKS1,
                                            extra=None)
@@ -175,27 +196,27 @@ class CryptoTestFormat(CryptoTestCase):
 
         # the simple case with password
         pw_ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, pw_ctx, 0)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, pw_ctx, 0)
         self.assertTrue(succ)
 
         # create with a keyfile
         kf_ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, kf_ctx, 0)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, kf_ctx, 0)
         self.assertTrue(succ)
 
         # the simple case with password blob
         bl_ctx = BlockDev.CryptoKeyslotContext(passphrase=[ord(c) for c in PASSWD])
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, bl_ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, bl_ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, None)
         self.assertTrue(succ)
 
         # simple case with extra options
         extra = BlockDev.CryptoLUKSExtra(label="blockdevLUKS")
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, pw_ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, pw_ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, extra)
         self.assertTrue(succ)
 
-        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_dev)
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
         m = re.search(r"Label:\s*(\S+)\s*", out)
         if not m or len(m.groups()) != 1:
             self.fail("Failed to get label information from:\n%s %s" % (out, err))
@@ -204,24 +225,42 @@ class CryptoTestFormat(CryptoTestCase):
         # different key derivation function
         pbkdf = BlockDev.CryptoLUKSPBKDF(type="pbkdf2")
         extra = BlockDev.CryptoLUKSExtra(pbkdf=pbkdf)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, pw_ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, pw_ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, extra)
         self.assertTrue(succ)
 
-        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_dev)
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
         m = re.search(r"PBKDF:\s*(\S+)\s*", out)
         if not m or len(m.groups()) != 1:
             self.fail("Failed to get pbkdf information from:\n%s %s" % (out, err))
         self.assertEqual(m.group(1), "pbkdf2")
 
-    def _is_fips_enabled(self):
-        if not os.path.exists("/proc/sys/crypto/fips_enabled"):
-            # if the file doesn't exist, we are definitely not in FIPS mode
-            return False
+    @tag_test(TestTags.SLOW, TestTags.CORE)
+    def test_luks1_format_pbkdf_options(self):
+        """Verify that formatting device as LUKS 1 with extra PBKDF arguments works"""
 
-        with open("/proc/sys/crypto/fips_enabled", "r") as f:
-            enabled = f.read()
-        return enabled.strip() == "1"
+        # different options for pbkdf2 -- all parameters set
+        pbkdf = BlockDev.CryptoLUKSPBKDF(type="pbkdf2", iterations=1000)
+        extra = BlockDev.CryptoLUKSExtra(pbkdf=pbkdf)
+
+        ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, ctx, 0,
+                                           BlockDev.CryptoLUKSVersion.LUKS1, extra)
+        self.assertTrue(succ)
+
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
+        m = re.search(r"MK iterations:\s*(\d+)\s*", out)
+        if not m or len(m.groups()) != 1:
+            self.fail("Failed to get pbkdf information from:\n%s %s" % (out, err))
+        self.assertEqual(int(m.group(1)), 1000)
+
+        # invalid PBKDF
+        pbkdf = BlockDev.CryptoLUKSPBKDF(type="argon2id")
+        extra = BlockDev.CryptoLUKSExtra(pbkdf=pbkdf)
+        with self.assertRaisesRegex(GLib.GError, "Invalid pbkdf specified"):
+            BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, ctx, 0,
+                                        BlockDev.CryptoLUKSVersion.LUKS1, extra)
+        self.assertTrue(succ)
 
     @tag_test(TestTags.SLOW, TestTags.CORE)
     def test_luks2_format_pbkdf_options(self):
@@ -235,11 +274,11 @@ class CryptoTestFormat(CryptoTestCase):
         extra = BlockDev.CryptoLUKSExtra(pbkdf=pbkdf)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, extra)
         self.assertTrue(succ)
 
-        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_dev)
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
         m = re.search(r"PBKDF:\s*(\S+)\s*", out)
         if not m or len(m.groups()) != 1:
             self.fail("Failed to get pbkdf information from:\n%s %s" % (out, err))
@@ -264,11 +303,11 @@ class CryptoTestFormat(CryptoTestCase):
         # different options for argon2 -- only memory set
         pbkdf = BlockDev.CryptoLUKSPBKDF(max_memory_kb=100*1024)
         extra = BlockDev.CryptoLUKSExtra(pbkdf=pbkdf)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, extra)
         self.assertTrue(succ)
 
-        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_dev)
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
         m = re.search(r"Memory:\s*(\d+)\s*", out)
         if not m or len(m.groups()) != 1:
             self.fail("Failed to get pbkdf information from:\n%s %s" % (out, err))
@@ -279,11 +318,11 @@ class CryptoTestFormat(CryptoTestCase):
         # different options for argon2 -- only miterations set
         pbkdf = BlockDev.CryptoLUKSPBKDF(iterations=5)
         extra = BlockDev.CryptoLUKSExtra(pbkdf=pbkdf)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, extra)
         self.assertTrue(succ)
 
-        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_dev)
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
         m = re.search(r"Time cost:\s*(\d+)\s*", out)
         if not m or len(m.groups()) != 1:
             self.fail("Failed to get pbkdf information from:\n%s %s" % (out, err))
@@ -305,24 +344,24 @@ class CryptoTestFormat(CryptoTestCase):
 
         # aes-xts: key size should default to 512
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 0, ctx, 0)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 0, ctx, 0)
         self.assertTrue(succ)
 
-        key_size = self._get_luks1_key_size(self.loop_dev)
+        key_size = self._get_luks1_key_size(self.loop_devs[0])
         self.assertEqual(key_size, 512)
 
         # aes-cbc: key size should default to 256
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-cbc-essiv:sha256", 0, ctx, 0)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-cbc-essiv:sha256", 0, ctx, 0)
         self.assertTrue(succ)
 
-        key_size = self._get_luks1_key_size(self.loop_dev)
+        key_size = self._get_luks1_key_size(self.loop_devs[0])
         self.assertEqual(key_size, 256)
 
         # try specifying key size for aes-xts
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-xts-plain64", 256, ctx, 0)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-xts-plain64", 256, ctx, 0)
         self.assertTrue(succ)
 
-        key_size = self._get_luks1_key_size(self.loop_dev)
+        key_size = self._get_luks1_key_size(self.loop_devs[0])
         self.assertEqual(key_size, 256)
 
 
@@ -341,10 +380,10 @@ class CryptoTestResize(CryptoTestCase):
         """Verify that resizing LUKS device works"""
 
         # the simple case with password
-        self._luks_format(self.loop_dev, PASSWD)
+        self._luks_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         # resize to 512 KiB (1024 * 512B sectors)
@@ -363,10 +402,10 @@ class CryptoTestResize(CryptoTestCase):
         """Verify that resizing LUKS 2 device works"""
 
         # the simple case with password
-        self._luks2_format(self.loop_dev, PASSWD, self.keyfile)
+        self._luks2_format(self.loop_devs[0], PASSWD, self.keyfile, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         # resize without passphrase should fail if key is saved in keyring
@@ -390,7 +429,7 @@ class CryptoTestOpenClose(CryptoTestCase
     def _luks_open_close(self, create_fn):
         """Verify that opening/closing LUKS device works"""
 
-        create_fn(self.loop_dev, PASSWD, self.keyfile)
+        create_fn(self.loop_devs[0], PASSWD, self.keyfile, fast_pbkdf=True)
 
         with self.assertRaises(GLib.GError):
             ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
@@ -398,23 +437,23 @@ class CryptoTestOpenClose(CryptoTestCase
 
         with self.assertRaisesRegex(GLib.GError, r"Device name cannot contain '/' character"):
             ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-            BlockDev.crypto_luks_open(self.loop_dev, "libblockdev/TestLUKS", ctx, False)
+            BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdev/TestLUKS", ctx, False)
 
 
         with self.assertRaisesRegex(GLib.GError, r"Incorrect passphrase"):
             ctx = BlockDev.CryptoKeyslotContext(passphrase="wrong-passphrase")
-            BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+            BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
 
         with self.assertRaises(GLib.GError):
             ctx = BlockDev.CryptoKeyslotContext(keyfile="wrong-keyfile")
-            BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+            BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
 
         with self.assertRaisesRegex(GLib.GError, "Only .* context types are valid for LUKS open"):
             ctx = BlockDev.CryptoKeyslotContext(volume_key=list(secrets.token_bytes(64)))
-            BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+            BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         # use the full /dev/mapper/ path
@@ -422,7 +461,7 @@ class CryptoTestOpenClose(CryptoTestCase
         self.assertTrue(succ)
 
         ctx = BlockDev.CryptoKeyslotContext(keyfile=self.keyfile)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         # use just the LUKS device name
@@ -430,7 +469,7 @@ class CryptoTestOpenClose(CryptoTestCase
         self.assertTrue(succ)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=[ord(c) for c in PASSWD])
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         succ = BlockDev.crypto_luks_close("/dev/mapper/libblockdevTestLUKS")
@@ -441,7 +480,7 @@ class CryptoTestOpenClose(CryptoTestCase
         self.assertTrue(succ)
 
         ctx = BlockDev.CryptoKeyslotContext(keyring="myshinylittlekey")
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx)
         self.assertTrue(succ)
 
         succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
@@ -459,10 +498,10 @@ class CryptoTestOpenClose(CryptoTestCase
     def test_luks2_open_close_non_ascii_passphrase(self):
         passphrase = "šššššššš"
 
-        self._luks2_format(self.loop_dev, passphrase)
+        self._luks2_format(self.loop_devs[0], passphrase, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=passphrase)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
@@ -474,7 +513,7 @@ class CryptoTestOpenClose(CryptoTestCase
             f.flush()
 
             ctx = BlockDev.CryptoKeyslotContext(keyfile=f.name)
-            succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+            succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
             self.assertTrue(succ)
 
             succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
@@ -485,66 +524,51 @@ class CryptoTestOpenClose(CryptoTestCase
         self.assertTrue(succ)
 
         ctx = BlockDev.CryptoKeyslotContext(keyring="myshinylittlekey")
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx)
         self.assertTrue(succ)
 
         succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
         self.assertTrue(succ)
 
 
-class CryptoTestAddKey(CryptoTestCase):
-    def _add_key(self, create_fn):
-        """Verify that adding key to LUKS device works"""
+class CryptoTestAddRemoveKey(CryptoTestCase):
+    def _remove_key(self, create_fn):
+        """Verify that adding/removing key to/from LUKS device works"""
 
-        create_fn(self.loop_dev, PASSWD, None)
+        create_fn(self.loop_devs[0], PASSWD, None, fast_pbkdf=True)
 
+        # add key, wrong passphrase
         with self.assertRaises(GLib.GError):
             ctx = BlockDev.CryptoKeyslotContext(passphrase="wrong-passphrase")
             nctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD2)
-            BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx)
-
-        ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        nctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD2)
-        succ = BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx)
-        self.assertTrue(succ)
-
-    @tag_test(TestTags.SLOW)
-    def test_luks_add_key(self):
-        self._add_key(self._luks_format)
-
-    @tag_test(TestTags.SLOW)
-    def test_luks2_add_key(self):
-        self._add_key(self._luks2_format)
-
-class CryptoTestRemoveKey(CryptoTestCase):
-    def _remove_key(self, create_fn):
-        """Verify that removing key from LUKS device works"""
-
-        create_fn(self.loop_dev, PASSWD, None)
+            BlockDev.crypto_luks_add_key(self.loop_devs[0], ctx, nctx)
 
+        # add key, correct passphrase
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
         nctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD2)
-        succ = BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx)
+        succ = BlockDev.crypto_luks_add_key(self.loop_devs[0], ctx, nctx)
         self.assertTrue(succ)
 
         nctx2 = BlockDev.CryptoKeyslotContext(passphrase=PASSWD3)
-        succ = BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx2)
+        succ = BlockDev.crypto_luks_add_key(self.loop_devs[0], ctx, nctx2)
         self.assertTrue(succ)
 
         nctx3 = BlockDev.CryptoKeyslotContext(keyfile=self.keyfile)
-        succ = BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx3)
+        succ = BlockDev.crypto_luks_add_key(self.loop_devs[0], ctx, nctx3)
 
+        # remove key, wrong passphrase
         with self.assertRaises(GLib.GError):
             wctx = BlockDev.CryptoKeyslotContext(passphrase="wrong-passphrase")
-            BlockDev.crypto_luks_remove_key(self.loop_dev, wctx)
+            BlockDev.crypto_luks_remove_key(self.loop_devs[0], wctx)
 
-        succ = BlockDev.crypto_luks_remove_key(self.loop_dev, ctx)
+        # remove key, correct passphrase
+        succ = BlockDev.crypto_luks_remove_key(self.loop_devs[0], ctx)
         self.assertTrue(succ)
 
-        succ = BlockDev.crypto_luks_remove_key(self.loop_dev, nctx2)
+        succ = BlockDev.crypto_luks_remove_key(self.loop_devs[0], nctx2)
         self.assertTrue(succ)
 
-        succ = BlockDev.crypto_luks_remove_key(self.loop_dev, nctx3)
+        succ = BlockDev.crypto_luks_remove_key(self.loop_devs[0], nctx3)
         self.assertTrue(succ)
 
     @tag_test(TestTags.SLOW)
@@ -572,13 +596,13 @@ class CryptoTestErrorLocale(CryptoTestCa
         """Verify that the error msg is locale agnostic"""
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, None, 0, ctx, 0)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], None, 0, ctx, 0)
         self.assertTrue(succ)
 
         locale.setlocale(locale.LC_ALL, "cs_CZ.UTF-8")
         try:
             ctx = BlockDev.CryptoKeyslotContext(passphrase="wrong-passphrase")
-            BlockDev.crypto_luks_remove_key(self.loop_dev, ctx)
+            BlockDev.crypto_luks_remove_key(self.loop_devs[0], ctx)
         except GLib.GError as e:
             self.assertIn("Operation not permitted", str(e))
 
@@ -586,16 +610,16 @@ class CryptoTestChangeKey(CryptoTestCase
     def _change_key(self, create_fn):
         """Verify that changing key in LUKS device works"""
 
-        create_fn(self.loop_dev, PASSWD, None)
+        create_fn(self.loop_devs[0], PASSWD, None, fast_pbkdf=True)
 
         with self.assertRaisesRegex(GLib.GError, r"No keyslot with given passphrase found."):
             ctx = BlockDev.CryptoKeyslotContext(passphrase="wrong-passphrase")
             nctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD2)
-            BlockDev.crypto_luks_change_key(self.loop_dev, ctx, nctx)
+            BlockDev.crypto_luks_change_key(self.loop_devs[0], ctx, nctx)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
         nctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD2)
-        succ = BlockDev.crypto_luks_change_key(self.loop_dev, ctx, nctx)
+        succ = BlockDev.crypto_luks_change_key(self.loop_devs[0], ctx, nctx)
         self.assertTrue(succ)
 
     @tag_test(TestTags.SLOW)
@@ -606,28 +630,6 @@ class CryptoTestChangeKey(CryptoTestCase
     def test_luks2_change_key(self):
         self._change_key(self._luks2_format)
 
-class CryptoTestIsLuks(CryptoTestCase):
-    def _is_luks(self, create_fn):
-        """Verify that LUKS device recognition works"""
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.crypto_device_is_luks("/non/existing/device")
-
-        create_fn(self.loop_dev, PASSWD, None)
-
-        is_luks = BlockDev.crypto_device_is_luks(self.loop_dev)
-        self.assertTrue(is_luks)
-
-        is_luks = BlockDev.crypto_device_is_luks(self.loop_dev2)
-        self.assertFalse(is_luks)
-
-    @tag_test(TestTags.SLOW)
-    def test_is_luks(self):
-        self._is_luks(self._luks_format)
-
-    @tag_test(TestTags.SLOW)
-    def test_is_luks2(self):
-        self._is_luks(self._luks2_format)
 
 class CryptoTestLuksStatus(CryptoTestCase):
     def _luks_status(self, create_fn):
@@ -636,10 +638,10 @@ class CryptoTestLuksStatus(CryptoTestCas
         with self.assertRaises(GLib.GError):
             BlockDev.crypto_luks_status("/non/existing/device")
 
-        create_fn(self.loop_dev, PASSWD, None)
+        create_fn(self.loop_devs[0], PASSWD, None, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         # use the full /dev/mapper path
@@ -668,10 +670,10 @@ class CryptoTestLuksOpenRW(CryptoTestCas
     def _luks_open_rw(self, create_fn):
         """Verify that a LUKS device can be activated as RW as well as RO"""
 
-        create_fn(self.loop_dev, PASSWD, None)
+        create_fn(self.loop_devs[0], PASSWD, None, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         # tests that we can write something to the raw LUKS device
@@ -682,7 +684,7 @@ class CryptoTestLuksOpenRW(CryptoTestCas
         self.assertTrue(succ)
 
         # now try the same with LUKS device opened as RO
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, True)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, True)
         self.assertTrue(succ)
 
         # tests that we can write something to the raw LUKS device
@@ -704,8 +706,7 @@ class CryptoTestEscrow(CryptoTestCase):
     def setUp(self):
         # I am not able to generate a self-signed certificate that would work in FIPS
         # so let's just skip this for now
-        fips = read_file("/proc/sys/crypto/fips_enabled")
-        if int(fips) == 1:
+        if self._is_fips_enabled():
             self.skipTest("Skipping escrow tests in FIPS mode")
 
         super(CryptoTestEscrow, self).setUp()
@@ -740,17 +741,17 @@ class CryptoTestEscrow(CryptoTestCase):
     def test_escrow_packet(self):
         """Verify that an escrow packet can be created for a device"""
 
-        self._luks_format(self.loop_dev, PASSWD)
+        self._luks_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
 
         escrow_dir = tempfile.mkdtemp(prefix='libblockdev_test_escrow')
         self.addCleanup(shutil.rmtree, escrow_dir)
         with open(self.public_cert, 'rb') as cert_file:
-            succ = BlockDev.crypto_escrow_device(self.loop_dev, PASSWD, cert_file.read(),
+            succ = BlockDev.crypto_escrow_device(self.loop_devs[0], PASSWD, cert_file.read(),
                     escrow_dir, None)
         self.assertTrue(succ)
 
         # Find the escrow packet
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         escrow_packet_file = '%s/%s-escrow' % (escrow_dir, info.uuid)
         self.assertTrue(os.path.isfile(escrow_packet_file))
 
@@ -769,7 +770,7 @@ class CryptoTestEscrow(CryptoTestCase):
 
         # Restore access to the volume
         # PASSWD3 is the new passphrase for the LUKS device
-        p = subprocess.Popen(['volume_key', '--restore', '-b', self.loop_dev,
+        p = subprocess.Popen(['volume_key', '--restore', '-b', self.loop_devs[0],
             '%s/escrow-out' % escrow_dir], stdin=subprocess.PIPE)
         p.communicate(input=('%s\0%s\0%s\0' % (PASSWD2, PASSWD3, PASSWD3)).encode('utf-8'))
         if p.returncode != 0:
@@ -777,24 +778,24 @@ class CryptoTestEscrow(CryptoTestCase):
 
         # Open the volume with the new passphrase
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD3)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, 'libblockdevTestLUKS', ctx)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], 'libblockdevTestLUKS', ctx)
         self.assertTrue(succ)
 
     @tag_test(TestTags.SLOW)
     def test_backup_passphrase(self):
         """Verify that a backup passphrase can be created for a device"""
-        self._luks_format(self.loop_dev, PASSWD)
+        self._luks_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
 
         escrow_dir = tempfile.mkdtemp(prefix='libblockdev_test_escrow')
         self.addCleanup(shutil.rmtree, escrow_dir)
         backup_passphrase = BlockDev.crypto_generate_backup_passphrase()
         with open(self.public_cert, 'rb') as cert_file:
-            succ = BlockDev.crypto_escrow_device(self.loop_dev, PASSWD, cert_file.read(),
+            succ = BlockDev.crypto_escrow_device(self.loop_devs[0], PASSWD, cert_file.read(),
                     escrow_dir, backup_passphrase)
         self.assertTrue(succ)
 
         # Find the backup passphrase
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         escrow_backup_passphrase = "%s/%s-escrow-backup-passphrase" % (escrow_dir, info.uuid)
         self.assertTrue(os.path.isfile(escrow_backup_passphrase))
 
@@ -809,16 +810,16 @@ class CryptoTestEscrow(CryptoTestCase):
 
         # Check that the backup passphrase works
         ctx = BlockDev.CryptoKeyslotContext(passphrase=backup_passphrase)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, 'libblockdevTestLUKS', ctx)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], 'libblockdevTestLUKS', ctx)
         self.assertTrue(succ)
 
 class CryptoTestSuspendResume(CryptoTestCase):
     def _luks_suspend_resume(self, create_fn):
 
-        create_fn(self.loop_dev, PASSWD, self.keyfile)
+        create_fn(self.loop_devs[0], PASSWD, self.keyfile, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx)
         self.assertTrue(succ)
 
         with self.assertRaises(GLib.GError):
@@ -883,11 +884,11 @@ class CryptoTestSuspendResume(CryptoTest
 class CryptoTestKillSlot(CryptoTestCase):
     def _luks_kill_slot(self, create_fn):
 
-        create_fn(self.loop_dev, PASSWD, None)
+        create_fn(self.loop_devs[0], PASSWD, None, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
         nctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD2)
-        succ = BlockDev.crypto_luks_add_key(self.loop_dev, ctx, nctx)
+        succ = BlockDev.crypto_luks_add_key(self.loop_devs[0], ctx, nctx)
         self.assertTrue(succ)
 
         with self.assertRaises(GLib.GError):
@@ -895,22 +896,22 @@ class CryptoTestKillSlot(CryptoTestCase)
 
         # invalid slot
         with self.assertRaises(GLib.GError):
-            BlockDev.crypto_luks_kill_slot(self.loop_dev, -1)
+            BlockDev.crypto_luks_kill_slot(self.loop_devs[0], -1)
 
         # unused slot
         with self.assertRaises(GLib.GError):
-            BlockDev.crypto_luks_kill_slot(self.loop_dev, 2)
+            BlockDev.crypto_luks_kill_slot(self.loop_devs[0], 2)
 
         # destroy second keyslot
-        succ = BlockDev.crypto_luks_kill_slot(self.loop_dev, 1)
+        succ = BlockDev.crypto_luks_kill_slot(self.loop_devs[0], 1)
         self.assertTrue(succ)
 
         # opening with the second passphrase should fail
         with self.assertRaises(GLib.GError):
-            BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", nctx)
+            BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", nctx)
 
         # opening with passphrase should still work
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx)
         self.assertTrue(succ)
 
         succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
@@ -935,37 +936,37 @@ class CryptoTestHeaderBackupRestore(Cryp
         self.addCleanup(shutil.rmtree, self.backup_dir)
 
     def _luks_header_backup_restore(self, create_fn):
-        create_fn(self.loop_dev, PASSWD, None)
+        create_fn(self.loop_devs[0], PASSWD, None, fast_pbkdf=True)
 
         backup_file = os.path.join(self.backup_dir, "luks-header.txt")
 
-        succ = BlockDev.crypto_luks_header_backup(self.loop_dev, backup_file)
+        succ = BlockDev.crypto_luks_header_backup(self.loop_devs[0], backup_file)
         self.assertTrue(succ)
         self.assertTrue(os.path.isfile(backup_file))
 
         # now completely destroy the luks header
-        ret, out, err = run_command("cryptsetup erase %s -q && wipefs -a %s" % (self.loop_dev, self.loop_dev))
+        ret, out, err = run_command("cryptsetup erase %s -q && wipefs -a %s" % (self.loop_devs[0], self.loop_devs[0]))
         if ret != 0:
-            self.fail("Failed to erase LUKS header from %s:\n%s %s" % (self.loop_dev, out, err))
+            self.fail("Failed to erase LUKS header from %s:\n%s %s" % (self.loop_devs[0], out, err))
 
-        _ret, fstype, _err = run_command("blkid -p -ovalue -sTYPE %s" % self.loop_dev)
+        _ret, fstype, _err = run_command("blkid -p -ovalue -sTYPE %s" % self.loop_devs[0])
         self.assertFalse(fstype)  # false == empty
 
         # header is destroyed, should not be possible to open
         with self.assertRaises(GLib.GError):
             ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-            BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx)
+            BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx)
 
         # and restore the header back
-        succ = BlockDev.crypto_luks_header_restore(self.loop_dev, backup_file)
+        succ = BlockDev.crypto_luks_header_restore(self.loop_devs[0], backup_file)
         self.assertTrue(succ)
 
-        _ret, fstype, _err = run_command("blkid -p -ovalue -sTYPE %s" % self.loop_dev)
+        _ret, fstype, _err = run_command("blkid -p -ovalue -sTYPE %s" % self.loop_devs[0])
         self.assertEqual(fstype, "crypto_LUKS")
 
         # opening should now work
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx)
         self.assertTrue(succ)
 
         succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
@@ -982,30 +983,31 @@ class CryptoTestHeaderBackupRestore(Cryp
         self._luks_header_backup_restore(self._luks2_format)
 
 class CryptoTestInfo(CryptoTestCase):
+    _num_devices = 2
 
     def _verify_luks_info(self, info, version):
         self.assertIsNotNone(info)
         self.assertEqual(info.version, version)
         self.assertEqual(info.cipher, "aes")
         self.assertEqual(info.mode, "cbc-essiv:sha256")
-        self.assertEqual(info.backing_device, self.loop_dev)
+        self.assertEqual(info.backing_device, self.loop_devs[0])
 
-        _ret, uuid, _err = run_command("blkid -p -ovalue -sUUID %s" % self.loop_dev)
+        _ret, uuid, _err = run_command("blkid -p -ovalue -sUUID %s" % self.loop_devs[0])
         self.assertEqual(info.uuid, uuid)
 
-        ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_dev)
+        ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
         if ret != 0:
-            self.fail("Failed to get LUKS header from %s:\n%s %s" % (self.loop_dev, out, err))
+            self.fail("Failed to get LUKS header from %s:\n%s %s" % (self.loop_devs[0], out, err))
 
         if version == BlockDev.CryptoLUKSVersion.LUKS1:
             m = re.search(r"Payload offset:\s*([0-9]+)", out)
             if m is None:
-                self.fail("Failed to get LUKS offset information from %s:\n%s %s" % (self.loop_dev, out, err))
+                self.fail("Failed to get LUKS offset information from %s:\n%s %s" % (self.loop_devs[0], out, err))
             offset = int(m.group(1)) * 512
         else:
             m = re.search(r"offset:\s*([0-9]+)\s*\[bytes\]", out)
             if m is None:
-                self.fail("Failed to get LUKS 2 offset information from %s:\n%s %s" % (self.loop_dev, out, err))
+                self.fail("Failed to get LUKS 2 offset information from %s:\n%s %s" % (self.loop_devs[0], out, err))
             offset = int(m.group(1))
 
         self.assertEqual(info.metadata_size, offset)
@@ -1015,13 +1017,13 @@ class CryptoTestInfo(CryptoTestCase):
         """Verify that we can get information about a LUKS device"""
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-cbc-essiv:sha256", 256, ctx, 0)
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-cbc-essiv:sha256", 256, ctx, 0)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self._verify_luks_info(info, BlockDev.CryptoLUKSVersion.LUKS1)
 
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         info = BlockDev.crypto_luks_info("libblockdevTestLUKS")
@@ -1033,6 +1035,13 @@ class CryptoTestInfo(CryptoTestCase):
         succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
         self.assertTrue(succ)
 
+        # test the crypto_device_is_luks function too
+        is_luks = BlockDev.crypto_device_is_luks(self.loop_devs[0])
+        self.assertTrue(is_luks)
+
+        is_luks = BlockDev.crypto_device_is_luks(self.loop_devs[1])
+        self.assertFalse(is_luks)
+
     @tag_test(TestTags.SLOW, TestTags.CORE)
     def test_luks2_info(self):
         """Verify that we can get information about a LUKS 2 device"""
@@ -1041,14 +1050,14 @@ class CryptoTestInfo(CryptoTestCase):
         extra.sector_size = 4096
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-cbc-essiv:sha256", 256, ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-cbc-essiv:sha256", 256, ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, extra)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self._verify_luks_info(info, BlockDev.CryptoLUKSVersion.LUKS2)
 
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         info = BlockDev.crypto_luks_info("libblockdevTestLUKS")
@@ -1060,103 +1069,117 @@ class CryptoTestInfo(CryptoTestCase):
         succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
         self.assertTrue(succ)
 
+        # test the crypto_device_is_luks function too
+        is_luks = BlockDev.crypto_device_is_luks(self.loop_devs[0])
+        self.assertTrue(is_luks)
 
-class CryptoTestSetLabel(CryptoTestCase):
-
-    label = "aaaaaa"
-    subsystem = "bbbbbb"
-
-    @tag_test(TestTags.SLOW, TestTags.CORE)
-    def test_luks_set_label(self):
-        """Verify that we can set label on a LUKS device"""
-
-        self._luks_format(self.loop_dev, PASSWD)
-
-        with self.assertRaisesRegex(GLib.GError, r"Label can be set only on LUKS 2"):
-            BlockDev.crypto_luks_set_label(self.loop_dev, self.label, self.subsystem)
+        is_luks = BlockDev.crypto_device_is_luks(self.loop_devs[1])
+        self.assertFalse(is_luks)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
-        self.assertIsNotNone(info)
-        self.assertEqual(info.label, "")
-        self.assertEqual(info.subsystem, "")
 
-    @tag_test(TestTags.SLOW, TestTags.CORE)
-    def test_luks2_set_label(self):
-        """Verify that we can set label on a LUKS 2 device"""
+class CryptoTestSetLabelUuid(CryptoTestCase):
 
-        self._luks2_format(self.loop_dev, PASSWD)
+    label = "aaaaaa"
+    subsystem = "bbbbbb"
+    test_uuid = "4d7086c4-a4d3-432f-819e-73da03870df9"
 
-        succ = BlockDev.crypto_luks_set_label(self.loop_dev, self.label, self.subsystem)
+    def _test_set_label(self):
+        succ = BlockDev.crypto_luks_set_label(self.loop_devs[0], self.label, self.subsystem)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertEqual(info.label, self.label)
         self.assertEqual(info.subsystem, self.subsystem)
 
-        _ret, label, _err = run_command("blkid -p -ovalue -sLABEL %s" % self.loop_dev)
+        _ret, label, _err = run_command("blkid -p -ovalue -sLABEL %s" % self.loop_devs[0])
         self.assertEqual(label, self.label)
 
-        _ret, subsystem, _err = run_command("blkid -p -ovalue -sSUBSYSTEM %s" % self.loop_dev)
+        _ret, subsystem, _err = run_command("blkid -p -ovalue -sSUBSYSTEM %s" % self.loop_devs[0])
         self.assertEqual(subsystem, self.subsystem)
 
-        succ = BlockDev.crypto_luks_set_label(self.loop_dev, None, None)
+        succ = BlockDev.crypto_luks_set_label(self.loop_devs[0], None, None)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertEqual(info.label, "")
         self.assertEqual(info.subsystem, "")
 
-        _ret, label, _err = run_command("blkid -p -ovalue -sLABEL %s" % self.loop_dev)
+        _ret, label, _err = run_command("blkid -p -ovalue -sLABEL %s" % self.loop_devs[0])
         self.assertEqual(label, "")
-        _ret, subsystem, _err = run_command("blkid -p -ovalue -sSUBSYSTEM %s" % self.loop_dev)
+        _ret, subsystem, _err = run_command("blkid -p -ovalue -sSUBSYSTEM %s" % self.loop_devs[0])
         self.assertEqual(subsystem, "")
 
-
-class CryptoTestSetUuid(CryptoTestCase):
-
-    test_uuid = "4d7086c4-a4d3-432f-819e-73da03870df9"
-
-    @tag_test(TestTags.SLOW, TestTags.CORE)
-    def test_luks_set_uuid(self):
-        """Verify that we can set label on a LUKS device"""
-
-        self._luks_format(self.loop_dev, PASSWD)
-
-        succ = BlockDev.crypto_luks_set_uuid(self.loop_dev, self.test_uuid)
+    def _test_set_uuid(self):
+        succ = BlockDev.crypto_luks_set_uuid(self.loop_devs[0], self.test_uuid)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertEqual(info.uuid, self.test_uuid)
 
-        succ = BlockDev.crypto_luks_set_uuid(self.loop_dev, None)
+        succ = BlockDev.crypto_luks_set_uuid(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertNotEqual(info.uuid, self.test_uuid)
 
     @tag_test(TestTags.SLOW, TestTags.CORE)
-    def test_luks2_set_uuid(self):
-        """Verify that we can set label on a LUKS 2 device"""
+    def test_luks_set_label_uuid(self):
+        """Verify that we can set label and UUID on a LUKS device"""
 
-        self._luks2_format(self.loop_dev, PASSWD)
+        self._luks_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
 
-        succ = BlockDev.crypto_luks_set_uuid(self.loop_dev, self.test_uuid)
-        self.assertTrue(succ)
+        # setting label is not supported on LUKS1
+        with self.assertRaisesRegex(GLib.GError, r"Label can be set only on LUKS 2"):
+            BlockDev.crypto_luks_set_label(self.loop_devs[0], self.label, self.subsystem)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
-        self.assertEqual(info.uuid, self.test_uuid)
+        self.assertEqual(info.label, "")
+        self.assertEqual(info.subsystem, "")
+
+        # setting UUID is supported on LUKS1
+        self._test_set_uuid()
 
-        succ = BlockDev.crypto_luks_set_uuid(self.loop_dev, None)
+    @tag_test(TestTags.SLOW, TestTags.CORE)
+    def test_luks2_set_label_uuid(self):
+        """Verify that we can set label and UUID on a LUKS 2 device"""
+
+        self._luks2_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
+        self._test_set_label()
+        self._test_set_uuid()
+
+
+class CryptoTestSetPersistentFlags(CryptoTestCase):
+
+    @tag_test(TestTags.SLOW, TestTags.CORE)
+    def test_luks_set_persistent_flags(self):
+        """Verify that we can set flags on a LUKS device"""
+
+        self._luks_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
+
+        with self.assertRaisesRegex(GLib.GError, "Persistent flags can be set only on LUKS v2"):
+            BlockDev.crypto_luks_set_persistent_flags(self.loop_devs[0],
+                                                      BlockDev.CryptoLUKSPersistentFlags.ALLOW_DISCARDS)
+
+    @tag_test(TestTags.SLOW, TestTags.CORE)
+    def test_luks2_set_persistent_flags(self):
+        """Verify that we can set flags on a LUKS 2 device"""
+
+        self._luks2_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
+
+        succ = BlockDev.crypto_luks_set_persistent_flags(self.loop_devs[0],
+                                                         BlockDev.CryptoLUKSPersistentFlags.ALLOW_DISCARDS)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
-        self.assertIsNotNone(info)
-        self.assertNotEqual(info.uuid, self.test_uuid)
+        _ret, out, err = run_command("cryptsetup luksDump %s" % self.loop_devs[0])
+        m = re.search(r"Flags:\s*(\S+)\s*", out)
+        if not m or len(m.groups()) != 1:
+            self.fail("Failed to get label information from:\n%s %s" % (out, err))
+        self.assertEqual(m.group(1), "allow-discards")
 
 
 class CryptoTestConvert(CryptoTestCase):
@@ -1165,12 +1188,13 @@ class CryptoTestConvert(CryptoTestCase):
     def test_convert_luks1_to_luks2(self):
         """Verify that we can convert a device from LUKS1 format to LUKS2"""
 
-        self._luks_format(self.loop_dev, PASSWD, luks_version=BlockDev.CryptoLUKSVersion.LUKS1)
+        self._luks_format(self.loop_devs[0], PASSWD, luks_version=BlockDev.CryptoLUKSVersion.LUKS1,
+                          fast_pbkdf=True)
 
-        succ = BlockDev.crypto_luks_convert(self.loop_dev, BlockDev.CryptoLUKSVersion.LUKS2)
+        succ = BlockDev.crypto_luks_convert(self.loop_devs[0], BlockDev.CryptoLUKSVersion.LUKS2)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertEqual(info.version, BlockDev.CryptoLUKSVersion.LUKS2)
 
@@ -1178,59 +1202,57 @@ class CryptoTestConvert(CryptoTestCase):
     def test_convert_luks1_to_luks2_to_luks1(self):
         """Verify that we can convert a device from LUKS1 format to LUKS2 and back to LUKS1"""
 
-        self._luks_format(self.loop_dev, PASSWD, luks_version=BlockDev.CryptoLUKSVersion.LUKS1)
+        self._luks_format(self.loop_devs[0], PASSWD, luks_version=BlockDev.CryptoLUKSVersion.LUKS1,
+                          fast_pbkdf=True)
 
         # LUKS1 -> LUKS2
-        succ = BlockDev.crypto_luks_convert(self.loop_dev, BlockDev.CryptoLUKSVersion.LUKS2)
+        succ = BlockDev.crypto_luks_convert(self.loop_devs[0], BlockDev.CryptoLUKSVersion.LUKS2)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertEqual(info.version, BlockDev.CryptoLUKSVersion.LUKS2)
 
-        # LUKS2 -> LUKS1
-        succ = BlockDev.crypto_luks_convert(self.loop_dev, BlockDev.CryptoLUKSVersion.LUKS1)
-        self.assertTrue(succ)
-
-        info = BlockDev.crypto_luks_info(self.loop_dev)
-        self.assertIsNotNone(info)
-        self.assertEqual(info.version, BlockDev.CryptoLUKSVersion.LUKS1)
-
-    @tag_test(TestTags.SLOW, TestTags.CORE)
-    def test_convert_luks2_to_luks2_fails(self):
-        """Verify that conversion to the present format fails"""
-
-        self._luks_format(self.loop_dev, PASSWD, luks_version=BlockDev.CryptoLUKSVersion.LUKS2)
-
+        # LUKS2 -> LUKS2 (fail)
         with self.assertRaisesRegex(GLib.GError, r"Conversion to the LUKS2 type was requested, but device .* is "
                                                  r"already of type: LUKS2"):
-            BlockDev.crypto_luks_convert(self.loop_dev, BlockDev.CryptoLUKSVersion.LUKS2)
+            BlockDev.crypto_luks_convert(self.loop_devs[0], BlockDev.CryptoLUKSVersion.LUKS2)
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertEqual(info.version, BlockDev.CryptoLUKSVersion.LUKS2)
 
+        # LUKS2 -> LUKS1
+        succ = BlockDev.crypto_luks_convert(self.loop_devs[0], BlockDev.CryptoLUKSVersion.LUKS1)
+        self.assertTrue(succ)
+
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
+        self.assertIsNotNone(info)
+        self.assertEqual(info.version, BlockDev.CryptoLUKSVersion.LUKS1)
+
 
 class CryptoTestLuksSectorSize(CryptoTestCase):
+    _num_devices = 2
+
     def setUp(self):
         if not check_cryptsetup_version("2.4.0"):
             self.skipTest("cryptsetup encryption sector size not available, skipping.")
 
-        # we need a loop devices for this test case
+        # we need special loop devices for this test case
         self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("crypto_test", 1024**3)
-        self.dev_file2 = create_sparse_tempfile("crypto_test", 1024**3)
+        self.dev_files.append(create_sparse_tempfile("crypto_test", self._sparse_size))
+        self.dev_files.append(create_sparse_tempfile("crypto_test", self._sparse_size))
 
         # create a 4k sector loop device
-        succ, loop = BlockDev.loop_setup(self.dev_file, sector_size=4096)
+        succ, loop = BlockDev.loop_setup(self.dev_files[0], sector_size=4096)
         if not succ:
             raise RuntimeError("Failed to setup loop device for testing")
-        self.loop_dev = "/dev/%s" % loop
+        self.loop_devs.append("/dev/%s" % loop)
 
-        succ, loop = BlockDev.loop_setup(self.dev_file2)
+        succ, loop = BlockDev.loop_setup(self.dev_files[1])
         if not succ:
             raise RuntimeError("Failed to setup loop device for testing")
-        self.loop_dev2 = "/dev/%s" % loop
+        self.loop_devs.append("/dev/%s" % loop)
 
     def _clean_up(self):
         try:
@@ -1238,20 +1260,21 @@ class CryptoTestLuksSectorSize(CryptoTes
         except:
             pass
 
-        BlockDev.loop_teardown(self.loop_dev)
-        os.unlink(self.dev_file)
+        for i in range(self._num_devices):
+            BlockDev.loop_teardown(self.loop_devs[i])
+            os.unlink(self.dev_files[i])
 
-        BlockDev.loop_teardown(self.loop_dev2)
-        os.unlink(self.dev_file2)
+        self.dev_files.clear()
+        self.loop_devs.clear()
 
     @tag_test(TestTags.SLOW)
     def test_luks2_sector_size_autodetect(self):
         """Verify that we can autodetect 4k drives and set 4k sector size for them"""
         # format the 4k loop device, encryption sector size should default to 4096
-        self._luks2_format(self.loop_dev, PASSWD)
+        self._luks2_format(self.loop_devs[0], PASSWD, fast_pbkdf=True)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         info = BlockDev.crypto_luks_info("libblockdevTestLUKS")
@@ -1264,9 +1287,9 @@ class CryptoTestLuksSectorSize(CryptoTes
         self.assertTrue(succ)
 
         # with the 512 loop device, we should still get 512
-        self._luks2_format(self.loop_dev2, PASSWD)
+        self._luks2_format(self.loop_devs[1], PASSWD, fast_pbkdf=True)
 
-        succ = BlockDev.crypto_luks_open(self.loop_dev2, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[1], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         info = BlockDev.crypto_luks_info("libblockdevTestLUKS")
@@ -1291,15 +1314,15 @@ class CryptoTestLUKS2Integrity(CryptoTes
         extra.integrity = "hmac(sha256)"
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_format(self.loop_dev, "aes-cbc-essiv:sha256", 512, ctx, 0,
+        succ = BlockDev.crypto_luks_format(self.loop_devs[0], "aes-cbc-essiv:sha256", 512, ctx, 0,
                                            BlockDev.CryptoLUKSVersion.LUKS2, extra)
         self.assertTrue(succ)
 
-        info = BlockDev.crypto_integrity_info(self.loop_dev)
+        info = BlockDev.crypto_integrity_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertEqual(info.algorithm, "hmac(sha256)")
 
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         info = BlockDev.crypto_integrity_info("libblockdevTestLUKS")
@@ -1311,7 +1334,7 @@ class CryptoTestLUKS2Integrity(CryptoTes
         self.assertEqual(info.algorithm, "hmac(sha256)")
 
         # get integrity device dm name
-        _ret, int_name, _err = run_command('ls /sys/block/%s/holders/' % self.loop_dev.split("/")[-1])
+        _ret, int_name, _err = run_command('ls /sys/block/%s/holders/' % self.loop_devs[0].split("/")[-1])
         self.assertTrue(int_name)  # true == not empty
 
         tag_size = read_file("/sys/block/%s/integrity/tag_size" % int_name)
@@ -1334,20 +1357,20 @@ class CryptoTestLUKSToken(CryptoTestCase
         """Verify that we can get information about LUKS2 tokens"""
 
         # the simple case with password
-        self._luks2_format(self.loop_dev, PASSWD, None)
+        self._luks2_format(self.loop_devs[0], PASSWD, None, fast_pbkdf=True)
 
-        info = BlockDev.crypto_luks_token_info(self.loop_dev)
+        info = BlockDev.crypto_luks_token_info(self.loop_devs[0])
         self.assertListEqual(info, [])
 
         # add kernel keyring token using cryptsetup
-        ret, _out, err = run_command("cryptsetup token add --key-description aaaa %s" % self.loop_dev)
-        self.assertEqual(ret, 0, msg="Failed to add token to %s: %s" % (self.loop_dev, err))
+        ret, _out, err = run_command("cryptsetup token add --key-description aaaa %s" % self.loop_devs[0])
+        self.assertEqual(ret, 0, msg="Failed to add token to %s: %s" % (self.loop_devs[0], err))
 
-        info = BlockDev.crypto_luks_token_info(self.loop_dev)
+        info = BlockDev.crypto_luks_token_info(self.loop_devs[0])
         self._verify_token_info(info)
 
         ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)
-        succ = BlockDev.crypto_luks_open(self.loop_dev, "libblockdevTestLUKS", ctx, False)
+        succ = BlockDev.crypto_luks_open(self.loop_devs[0], "libblockdevTestLUKS", ctx, False)
         self.assertTrue(succ)
 
         info = BlockDev.crypto_luks_token_info("libblockdevTestLUKS")
@@ -1657,10 +1680,10 @@ class CryptoTestIntegrity(CryptoTestCase
 
     def test_integrity(self):
         # basic format+open+close test
-        succ = BlockDev.crypto_integrity_format(self.loop_dev, "sha256", False)
+        succ = BlockDev.crypto_integrity_format(self.loop_devs[0], "sha256", False)
         self.assertTrue(succ)
 
-        succ = BlockDev.crypto_integrity_open(self.loop_dev, self._dm_name, "sha256")
+        succ = BlockDev.crypto_integrity_open(self.loop_devs[0], self._dm_name, "sha256")
         self.assertTrue(succ)
         self.assertTrue(os.path.exists("/dev/mapper/%s" % self._dm_name))
 
@@ -1674,10 +1697,10 @@ class CryptoTestIntegrity(CryptoTestCase
         # same now with a keyed algorithm
         ctx = BlockDev.CryptoKeyslotContext(volume_key=list(secrets.token_bytes(64)))
 
-        succ = BlockDev.crypto_integrity_format(self.loop_dev, "hmac(sha256)", False, ctx)
+        succ = BlockDev.crypto_integrity_format(self.loop_devs[0], "hmac(sha256)", False, ctx)
         self.assertTrue(succ)
 
-        succ = BlockDev.crypto_integrity_open(self.loop_dev, self._dm_name, "hmac(sha256)", ctx)
+        succ = BlockDev.crypto_integrity_open(self.loop_devs[0], self._dm_name, "hmac(sha256)", ctx)
         self.assertTrue(succ)
         self.assertTrue(os.path.exists("/dev/mapper/%s" % self._dm_name))
 
@@ -1690,10 +1713,10 @@ class CryptoTestIntegrity(CryptoTestCase
 
         # same with some custom parameters
         extra = BlockDev.CryptoIntegrityExtra(sector_size=4096, interleave_sectors=65536)
-        succ = BlockDev.crypto_integrity_format(self.loop_dev, "crc32c", wipe=False, extra=extra)
+        succ = BlockDev.crypto_integrity_format(self.loop_devs[0], "crc32c", wipe=False, extra=extra)
         self.assertTrue(succ)
 
-        succ = BlockDev.crypto_integrity_open(self.loop_dev, self._dm_name, "crc32c")
+        succ = BlockDev.crypto_integrity_open(self.loop_devs[0], self._dm_name, "crc32c")
         self.assertTrue(succ)
         self.assertTrue(os.path.exists("/dev/mapper/%s" % self._dm_name))
 
@@ -1707,7 +1730,7 @@ class CryptoTestIntegrity(CryptoTestCase
         self.assertFalse(os.path.exists("/dev/mapper/%s" % self._dm_name))
 
         # open with flags
-        succ = BlockDev.crypto_integrity_open(self.loop_dev, self._dm_name, "crc32c",
+        succ = BlockDev.crypto_integrity_open(self.loop_devs[0], self._dm_name, "crc32c",
                                               flags=BlockDev.CryptoIntegrityOpenFlags.ALLOW_DISCARDS)
         self.assertTrue(succ)
         self.assertTrue(os.path.exists("/dev/mapper/%s" % self._dm_name))
@@ -1732,13 +1755,13 @@ class CryptoTestIntegrity(CryptoTestCase
         self.assertTrue(succ)
         self.addCleanup(BlockDev.utils_init_prog_reporting, None)
 
-        succ = BlockDev.crypto_integrity_format(self.loop_dev, "sha256", True)
+        succ = BlockDev.crypto_integrity_format(self.loop_devs[0], "sha256", True)
         self.assertTrue(succ)
 
         # at least one message "Integrity device wipe in progress" should be logged
         self.assertTrue(any(prog[1] == "Integrity device wipe in progress" for prog in progress_log))
 
-        succ = BlockDev.crypto_integrity_open(self.loop_dev, self._dm_name, "sha256")
+        succ = BlockDev.crypto_integrity_open(self.loop_devs[0], self._dm_name, "sha256")
         self.assertTrue(succ)
         self.assertTrue(os.path.exists("/dev/mapper/%s" % self._dm_name))
 
@@ -1759,24 +1782,24 @@ class CryptoTestLUKSOpal(CryptoTestCase)
     def test_luks_opal_sanity(self):
         """Basic sanity check for LUKS HW-OPAL support"""
         with self.assertRaisesRegex(GLib.GError, r"Failed to get opal status for the device"):
-            BlockDev.crypto_opal_is_supported(self.loop_dev)
+            BlockDev.crypto_opal_is_supported(self.loop_devs[0])
 
         with self.assertRaisesRegex(GLib.GError, r"OPAL doesn't seem to be supported on"):
-            BlockDev.crypto_opal_format(self.loop_dev, context=BlockDev.CryptoKeyslotContext(passphrase="aaaaa"),
+            BlockDev.crypto_opal_format(self.loop_devs[0], context=BlockDev.CryptoKeyslotContext(passphrase="aaaaa"),
                                         opal_context=BlockDev.CryptoKeyslotContext(passphrase="aaaaa"))
 
         # "normal" LUKS device
-        self._luks_format(self.loop_dev, PASSWD, None)
+        self._luks_format(self.loop_devs[0], PASSWD, None)
 
         with self.assertRaisesRegex(GLib.GError, r"isn't a LUKS HW-OPAL device"):
-            BlockDev.crypto_opal_wipe_device(self.loop_dev,
+            BlockDev.crypto_opal_wipe_device(self.loop_devs[0],
                                              BlockDev.CryptoKeyslotContext(passphrase="aaaaa"))
 
-        info = BlockDev.crypto_luks_info(self.loop_dev)
+        info = BlockDev.crypto_luks_info(self.loop_devs[0])
         self.assertEqual(info.hw_encryption, BlockDev.CryptoLUKSHWEncryptionType.SW_ONLY)
 
         with self.assertRaisesRegex(GLib.GError, r"OPAL doesn't seem to be supported on"):
-            BlockDev.crypto_opal_reset_device(self.loop_dev,
+            BlockDev.crypto_opal_reset_device(self.loop_devs[0],
                                               BlockDev.CryptoKeyslotContext(passphrase="aaaaa"))
 
     @unittest.skip("requires special hardware")
diff -pruN 3.3.1-3/tests/dm_test.py 3.4.0-1/tests/dm_test.py
--- 3.3.1-3/tests/dm_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/dm_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -25,7 +25,7 @@ class DevMapperTest(unittest.TestCase):
 class DevMapperPluginVersionCase(DevMapperTest):
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.DM), "libbd_dm.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.DM), "libbd_dm.so.3")
 
 class DevMapperTestCase(DevMapperTest):
 
diff -pruN 3.3.1-3/tests/fs_tests/btrfs_test.py 3.4.0-1/tests/fs_tests/btrfs_test.py
--- 3.3.1-3/tests/fs_tests/btrfs_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/btrfs_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -66,18 +66,18 @@ class BtrfsTestMkfs(BtrfsTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_btrfs_mkfs("/non/existing/device", None)
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "btrfs")
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
 
 class BtrfsMkfsWithLabel(BtrfsTestCase):
@@ -85,10 +85,10 @@ class BtrfsMkfsWithLabel(BtrfsTestCase):
         """Verify that it is possible to create a btrfs file system with label"""
 
         ea = BlockDev.ExtraArg.new("-L", "test_label")
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev, [ea])
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             fi = BlockDev.fs_btrfs_get_info(self.mount_dir)
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label")
@@ -98,10 +98,10 @@ class BtrfsTestCheck(BtrfsTestCase):
     def test_btrfs_check(self):
         """Verify that it is possible to check an btrfs file system"""
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_btrfs_check(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_check(self.loop_devs[0], None)
         self.assertTrue(succ)
 
 
@@ -109,10 +109,10 @@ class BtrfsTestRepair(BtrfsTestCase):
     def test_btrfs_repair(self):
         """Verify that it is possible to repair a btrfs file system"""
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_btrfs_repair(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_repair(self.loop_devs[0], None)
         self.assertTrue(succ)
 
 
@@ -120,10 +120,10 @@ class BtrfsGetInfo(BtrfsTestCase):
     def test_btrfs_get_info(self):
         """Verify that it is possible to get info about a btrfs file system"""
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             fi = BlockDev.fs_btrfs_get_info(self.mount_dir)
 
         self.assertTrue(fi)
@@ -139,10 +139,10 @@ class BtrfsSetLabel(BtrfsTestCase):
     def test_btrfs_set_label(self):
         """Verify that it is possible to set label of a btrfs file system"""
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             fi = BlockDev.fs_btrfs_get_info(self.mount_dir)
             self.assertTrue(fi)
             self.assertEqual(fi.label, "")
@@ -182,20 +182,20 @@ class BtrfsSetUUID(BtrfsTestCase):
     def test_btrfs_set_uuid(self):
         """Verify that it is possible to set UUID of an btrfs file system"""
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_btrfs_set_uuid(self.loop_dev, self.test_uuid)
+        succ = BlockDev.fs_btrfs_set_uuid(self.loop_devs[0], self.test_uuid)
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             fi = BlockDev.fs_btrfs_get_info(self.mount_dir)
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, self.test_uuid)
 
         # no uuid -> random
-        succ = BlockDev.fs_btrfs_set_uuid(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_set_uuid(self.loop_devs[0], None)
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             fi = BlockDev.fs_btrfs_get_info(self.mount_dir)
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
@@ -212,10 +212,10 @@ class BtrfsResize(BtrfsTestCase):
     def test_btrfs_resize(self):
         """Verify that it is possible to resize a btrfs file system"""
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             succ = BlockDev.fs_btrfs_resize(self.mount_dir, 300 * 1024**2)
             self.assertTrue(succ)
 
@@ -245,23 +245,24 @@ class BtrfsResize(BtrfsTestCase):
 
 
 class BtrfsMultiDevice(BtrfsTestCase):
+    num_devices = 2
 
     def _clean_up(self):
         utils.umount(self.mount_dir)
-        BlockDev.fs_wipe(self.loop_dev2, True)
+        BlockDev.fs_wipe(self.loop_devs[1], True)
 
         super(BtrfsMultiDevice, self)._clean_up()
 
     def test_btrfs_multidevice(self):
         """Verify that filesystem plugin returns errors when used on multidevice volumes"""
 
-        ret, _out, _err = utils.run_command("mkfs.btrfs %s %s" % (self.loop_dev, self.loop_dev2))
+        ret, _out, _err = utils.run_command("mkfs.btrfs %s %s" % (self.loop_devs[0], self.loop_devs[1]))
         self.assertEqual(ret, 0)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             with self.assertRaisesRegex(GLib.GError, "Filesystem plugin is not suitable for multidevice Btrfs volumes"):
                     BlockDev.fs_btrfs_get_info(self.mount_dir)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             with self.assertRaisesRegex(GLib.GError, "Filesystem plugin is not suitable for multidevice Btrfs volumes"):
                 BlockDev.fs_btrfs_resize(self.mount_dir, 0)
diff -pruN 3.3.1-3/tests/fs_tests/exfat_test.py 3.4.0-1/tests/fs_tests/exfat_test.py
--- 3.3.1-3/tests/fs_tests/exfat_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/exfat_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -102,18 +102,18 @@ class ExfatTestMkfs(ExfatTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_exfat_mkfs("/non/existing/device", None)
 
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev)
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "exfat")
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
 
 class ExfatMkfsWithLabel(ExfatTestCase):
@@ -121,10 +121,10 @@ class ExfatMkfsWithLabel(ExfatTestCase):
         """Verify that it is possible to create an exfat file system with label"""
 
         ea = BlockDev.ExtraArg.new("-n", "test_label")
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev, [ea])
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label")
 
@@ -133,10 +133,10 @@ class ExfatTestCheck(ExfatTestCase):
     def test_exfat_check(self):
         """Verify that it is possible to check an exfat file system"""
 
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev)
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_exfat_check(self.loop_dev)
+        succ = BlockDev.fs_exfat_check(self.loop_devs[0])
         self.assertTrue(succ)
 
 
@@ -144,10 +144,10 @@ class ExfatTestRepair(ExfatTestCase):
     def test_exfat_repair(self):
         """Verify that it is possible to repair an exfat file system"""
 
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev)
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_exfat_repair(self.loop_dev)
+        succ = BlockDev.fs_exfat_repair(self.loop_devs[0])
         self.assertTrue(succ)
 
 
@@ -155,10 +155,10 @@ class ExfatGetInfo(ExfatTestCase):
     def test_exfat_get_info(self):
         """Verify that it is possible to get info about an exfat file system"""
 
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
         # should be an non-empty string
@@ -172,28 +172,28 @@ class ExfatSetLabel(ExfatTestCase):
     def test_exfat_set_label(self):
         """Verify that it is possible to set label of an exfat file system"""
 
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
-        succ = BlockDev.fs_exfat_set_label(self.loop_dev, "test_label")
+        succ = BlockDev.fs_exfat_set_label(self.loop_devs[0], "test_label")
         self.assertTrue(succ)
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label")
 
-        succ = BlockDev.fs_exfat_set_label(self.loop_dev, "test_label2")
+        succ = BlockDev.fs_exfat_set_label(self.loop_devs[0], "test_label2")
         self.assertTrue(succ)
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label2")
 
-        succ = BlockDev.fs_exfat_set_label(self.loop_dev, "")
+        succ = BlockDev.fs_exfat_set_label(self.loop_devs[0], "")
         self.assertTrue(succ)
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
@@ -208,31 +208,31 @@ class ExfatSetUUID(ExfatTestCase):
     def test_exfat_set_uuid(self):
         """Verify that it is possible to set UUID/volume ID of an exfat file system"""
 
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev)
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_exfat_set_uuid(self.loop_dev, "0x2E24EC82")
+        succ = BlockDev.fs_exfat_set_uuid(self.loop_devs[0], "0x2E24EC82")
         self.assertTrue(succ)
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, "2E24-EC82")
 
-        succ = BlockDev.fs_exfat_set_uuid(self.loop_dev, "2E24EC82")
+        succ = BlockDev.fs_exfat_set_uuid(self.loop_devs[0], "2E24EC82")
         self.assertTrue(succ)
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, "2E24-EC82")
 
         # should be also support with the dash
-        succ = BlockDev.fs_exfat_set_uuid(self.loop_dev, "2E24-EC82")
+        succ = BlockDev.fs_exfat_set_uuid(self.loop_devs[0], "2E24-EC82")
         self.assertTrue(succ)
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, "2E24-EC82")
 
-        succ = BlockDev.fs_exfat_set_uuid(self.loop_dev, "")
+        succ = BlockDev.fs_exfat_set_uuid(self.loop_devs[0], "")
         self.assertTrue(succ)
-        fi = BlockDev.fs_exfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_exfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertTrue(fi.uuid)  # new random, not empty
         self.assertNotEqual(fi.uuid, "2E24-EC82")
diff -pruN 3.3.1-3/tests/fs_tests/ext_test.py 3.4.0-1/tests/fs_tests/ext_test.py
--- 3.3.1-3/tests/fs_tests/ext_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/ext_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -121,18 +121,18 @@ class ExtTestMkfs(ExtTestCase):
         with self.assertRaises(GLib.GError):
             mkfs_function("/non/existing/device", None)
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, ext_version)
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
     @tag_test(TestTags.CORE)
     def test_ext2_mkfs(self):
@@ -156,10 +156,10 @@ class ExtTestMkfs(ExtTestCase):
 class ExtMkfsWithLabel(ExtTestCase):
     def _test_ext_mkfs_with_label(self, mkfs_function, info_function):
         ea = BlockDev.ExtraArg.new("-L", "TEST_LABEL")
-        succ = mkfs_function(self.loop_dev, [ea])
+        succ = mkfs_function(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL")
 
@@ -181,10 +181,10 @@ class ExtMkfsWithLabel(ExtTestCase):
 
 class ExtTestCheck(ExtTestCase):
     def _test_ext_check(self, mkfs_function, check_function):
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = check_function(self.loop_dev, None)
+        succ = check_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
     def test_ext2_check(self):
@@ -205,17 +205,17 @@ class ExtTestCheck(ExtTestCase):
 
 class ExtTestRepair(ExtTestCase):
     def _test_ext_repair(self, mkfs_function, repair_function):
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = repair_function(self.loop_dev, False, None)
+        succ = repair_function(self.loop_devs[0], False, None)
         self.assertTrue(succ)
 
         # unsafe operations should work here too
-        succ = repair_function(self.loop_dev, True, None)
+        succ = repair_function(self.loop_devs[0], True, None)
         self.assertTrue(succ)
 
-        succ = repair_function(self.loop_dev, False, None)
+        succ = repair_function(self.loop_devs[0], False, None)
         self.assertTrue(succ)
 
     def test_ext2_repair(self):
@@ -236,10 +236,10 @@ class ExtTestRepair(ExtTestCase):
 
 class ExtGetInfo(ExtTestCase):
     def _test_ext_get_info(self, mkfs_function, info_function):
-        succ = BlockDev.fs_ext4_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_ext4_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_ext4_get_info(self.loop_dev)
+        fi = BlockDev.fs_ext4_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.block_size, 1024)
         self.assertEqual(fi.block_count, self.loop_size / 1024)
@@ -250,8 +250,8 @@ class ExtGetInfo(ExtTestCase):
         self.assertTrue(fi.uuid)
         self.assertTrue(fi.state, "clean")
 
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_ext4_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_ext4_get_info(self.loop_devs[0])
             self.assertEqual(fi.block_size, 1024)
             self.assertEqual(fi.block_count, self.loop_size / 1024)
             # at least 90 % should be available, so it should be reported
@@ -282,28 +282,28 @@ class ExtGetInfo(ExtTestCase):
 
 class ExtSetLabel(ExtTestCase):
     def _test_ext_set_label(self, mkfs_function, info_function, label_function, check_function):
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
-        succ = label_function(self.loop_dev, "TEST_LABEL")
+        succ = label_function(self.loop_devs[0], "TEST_LABEL")
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL")
 
-        succ = label_function(self.loop_dev, "TEST_LABEL2")
+        succ = label_function(self.loop_devs[0], "TEST_LABEL2")
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL2")
 
-        succ = label_function(self.loop_dev, "")
+        succ = label_function(self.loop_devs[0], "")
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
@@ -337,27 +337,27 @@ class ExtSetLabel(ExtTestCase):
 
 class ExtResize(ExtTestCase):
     def _test_ext_resize(self, mkfs_function, info_function, resize_function, minsize_function):
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.block_size, 1024)
         self.assertEqual(fi.block_count, self.loop_size / 1024)
         # at least 90 % should be available, so it should be reported
         self.assertGreater(fi.free_blocks, 0.90 * self.loop_size / 1024)
 
-        succ = resize_function(self.loop_dev, 50 * 1024**2, None)
+        succ = resize_function(self.loop_devs[0], 50 * 1024**2, None)
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.block_size, 1024)
         self.assertEqual(fi.block_count, 50 * 1024**2 / 1024)
 
         # resize back
-        succ = resize_function(self.loop_dev, self.loop_size, None)
+        succ = resize_function(self.loop_devs[0], self.loop_size, None)
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.block_size, 1024)
         self.assertEqual(fi.block_count, self.loop_size / 1024)
@@ -365,17 +365,17 @@ class ExtResize(ExtTestCase):
         self.assertGreater(fi.free_blocks, 0.90 * self.loop_size / 1024)
 
         # resize again
-        succ = resize_function(self.loop_dev, 50 * 1024**2, None)
+        succ = resize_function(self.loop_devs[0], 50 * 1024**2, None)
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.block_size, 1024)
         self.assertEqual(fi.block_count, 50 * 1024**2 / 1024)
 
         # resize back again, this time to maximum size
-        succ = resize_function(self.loop_dev, 0, None)
+        succ = resize_function(self.loop_devs[0], 0, None)
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.block_size, 1024)
         self.assertEqual(fi.block_count, self.loop_size / 1024)
@@ -383,10 +383,10 @@ class ExtResize(ExtTestCase):
         self.assertGreater(fi.free_blocks, 0.90 * self.loop_size / 1024)
 
         # get min size and resize to it
-        size = minsize_function(self.loop_dev)
+        size = minsize_function(self.loop_devs[0])
         self.assertNotEqual(size, 0)
 
-        succ = resize_function(self.loop_dev, size)
+        succ = resize_function(self.loop_devs[0], size)
         self.assertTrue(succ)
 
     def test_ext2_resize(self):
@@ -416,43 +416,43 @@ class ExtSetUUID(ExtTestCase):
     test_uuid = "4d7086c4-a4d3-432f-819e-73da03870df9"
 
     def _test_ext_set_uuid(self, mkfs_function, info_function, label_function, check_function):
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
 
-        succ = label_function(self.loop_dev, self.test_uuid)
+        succ = label_function(self.loop_devs[0], self.test_uuid)
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, self.test_uuid)
 
-        succ = label_function(self.loop_dev, "clear")
+        succ = label_function(self.loop_devs[0], "clear")
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, "")
 
-        succ = label_function(self.loop_dev, "random")
+        succ = label_function(self.loop_devs[0], "random")
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         random_uuid = fi.uuid
 
-        succ = label_function(self.loop_dev, "time")
+        succ = label_function(self.loop_devs[0], "time")
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         self.assertNotEqual(fi.uuid, random_uuid)
         time_uuid = fi.uuid
 
         # no UUID -> random
-        succ = label_function(self.loop_dev, None)
+        succ = label_function(self.loop_devs[0], None)
         self.assertTrue(succ)
-        fi = info_function(self.loop_dev)
+        fi = info_function(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         self.assertNotEqual(fi.uuid, time_uuid)
diff -pruN 3.3.1-3/tests/fs_tests/f2fs_test.py 3.4.0-1/tests/fs_tests/f2fs_test.py
--- 3.3.1-3/tests/fs_tests/f2fs_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/f2fs_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -142,18 +142,18 @@ class F2FSTestMkfs(F2FSTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_f2fs_mkfs("/non/existing/device", None)
 
-        succ = BlockDev.fs_f2fs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_f2fs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "f2fs")
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
 
 class F2FSMkfsWithLabel(F2FSTestCase):
@@ -167,10 +167,10 @@ class F2FSMkfsWithLabel(F2FSTestCase):
             BlockDev.fs_f2fs_check_label(513 * "a")
 
         ea = BlockDev.ExtraArg.new("-l", "TEST_LABEL")
-        succ = BlockDev.fs_f2fs_mkfs(self.loop_dev, [ea])
+        succ = BlockDev.fs_f2fs_mkfs(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL")
 
@@ -180,10 +180,10 @@ class F2FSMkfsWithFeatures(F2FSTestCase)
         """Verify that it is possible to create an f2fs file system with extra features enabled"""
 
         ea = BlockDev.ExtraArg.new("-O", "encrypt")
-        succ = BlockDev.fs_f2fs_mkfs(self.loop_dev, [ea])
+        succ = BlockDev.fs_f2fs_mkfs(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertTrue(fi.features & BlockDev.FSF2FSFeature.ENCRYPT)
 
@@ -192,14 +192,14 @@ class F2FSTestCheck(F2FSTestCase):
     def test_f2fs_check(self):
         """Verify that it is possible to check an f2fs file system"""
 
-        succ = BlockDev.fs_f2fs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_f2fs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         if not _check_fsck_f2fs_version():
             with self.assertRaisesRegex(GLib.GError, "Too low version of fsck.f2fs. At least 1.11.0 required."):
-                BlockDev.fs_f2fs_check(self.loop_dev, None)
+                BlockDev.fs_f2fs_check(self.loop_devs[0], None)
         else:
-            succ = BlockDev.fs_f2fs_check(self.loop_dev, None)
+            succ = BlockDev.fs_f2fs_check(self.loop_devs[0], None)
             self.assertTrue(succ)
 
 
@@ -207,10 +207,10 @@ class F2FSTestRepair(F2FSTestCase):
     def test_f2fs_repair(self):
         """Verify that it is possible to repair an f2fs file system"""
 
-        succ = BlockDev.fs_f2fs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_f2fs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_f2fs_repair(self.loop_dev, None)
+        succ = BlockDev.fs_f2fs_repair(self.loop_devs[0], None)
         self.assertTrue(succ)
 
 
@@ -218,10 +218,10 @@ class F2FSGetInfo(F2FSTestCase):
     def test_f2fs_get_info(self):
         """Verify that it is possible to get info about an f2fs file system"""
 
-        succ = BlockDev.fs_f2fs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_f2fs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
         # should be an non-empty string
@@ -233,47 +233,47 @@ class F2FSResize(F2FSTestCase):
     def test_f2fs_resize(self):
         """Verify that it is possible to resize an f2fs file system"""
 
-        succ = BlockDev.fs_f2fs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_f2fs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # shrink without the safe option -- should fail
         with self.assertRaises(GLib.GError):
-            BlockDev.fs_f2fs_resize(self.loop_dev, 100 * 1024**2 / 512, False)
+            BlockDev.fs_f2fs_resize(self.loop_devs[0], 100 * 1024**2 / 512, False)
 
         # if we can't shrink we'll just check it returns some sane error
         if not _can_resize_f2fs():
             with self.assertRaisesRegex(GLib.GError, "Too low version of resize.f2fs. At least 1.12.0 required."):
-                BlockDev.fs_f2fs_resize(self.loop_dev, 100 * 1024**2 / 512, True)
+                BlockDev.fs_f2fs_resize(self.loop_devs[0], 100 * 1024**2 / 512, True)
             return
 
-        succ = BlockDev.fs_f2fs_resize(self.loop_dev, 100 * 1024**2 / 512, True)
+        succ = BlockDev.fs_f2fs_resize(self.loop_devs[0], 100 * 1024**2 / 512, True)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         if fi.sector_size == 0:
             # XXX latest versions of dump.f2fs don't print the sector size
             self.skipTest("Cannot get sector size of the f2fs filesystem, skipping")
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         self.assertEqual(fi.sector_count * fi.sector_size, 100 * 1024**2)
 
         # grow
-        succ = BlockDev.fs_f2fs_resize(self.loop_dev, 120 * 1024**2 / 512, True)
+        succ = BlockDev.fs_f2fs_resize(self.loop_devs[0], 120 * 1024**2 / 512, True)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         self.assertEqual(fi.sector_count * fi.sector_size, 120 * 1024**2)
 
         # shrink again
-        succ = BlockDev.fs_f2fs_resize(self.loop_dev, 100 * 1024**2 / 512, True)
+        succ = BlockDev.fs_f2fs_resize(self.loop_devs[0], 100 * 1024**2 / 512, True)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         self.assertEqual(fi.sector_count * fi.sector_size, 100 * 1024**2)
 
         # resize to maximum size
-        succ = BlockDev.fs_f2fs_resize(self.loop_dev, 0, False)
+        succ = BlockDev.fs_f2fs_resize(self.loop_devs[0], 0, False)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_f2fs_get_info(self.loop_dev)
+        fi = BlockDev.fs_f2fs_get_info(self.loop_devs[0])
         self.assertEqual(fi.sector_count * fi.sector_size, self.loop_size)
diff -pruN 3.3.1-3/tests/fs_tests/fs_test.py 3.4.0-1/tests/fs_tests/fs_test.py
--- 3.3.1-3/tests/fs_tests/fs_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/fs_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -108,40 +108,40 @@ class FSNoDevTestCase(unittest.TestCase)
 class FSTestCase(FSNoDevTestCase):
 
     loop_size = 150 * 1024**2
+    num_devices = 1
+    loop_devs = []
+    dev_files = []
 
     def setUp(self):
         self.addCleanup(self._clean_up)
-        self.dev_file = utils.create_sparse_tempfile("fs_test", self.loop_size)
-        self.dev_file2 = utils.create_sparse_tempfile("fs_test", self.loop_size)
-        try:
-            self.loop_dev = utils.create_lio_device(self.dev_file)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev2 = utils.create_lio_device(self.dev_file2)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
 
-    def _clean_up(self):
-        try:
-            utils.delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
-
-        try:
-            utils.delete_lio_device(self.loop_dev2)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file2)
+        for i in range(self.num_devices):
+            dev_file = utils.create_sparse_tempfile("crypto_test", self.loop_size)
+            self.dev_files.append(dev_file)
+
+            try:
+                loop_dev = utils.create_lio_device(self.dev_files[i])
+                self.loop_devs.append(loop_dev)
+            except RuntimeError as e:
+                raise RuntimeError("Failed to setup loop device for testing: %s" % e)
 
+    def _clean_up(self):
         try:
             utils.umount(self.mount_dir)
         except:
             pass
 
+        for i in range(self.num_devices):
+            try:
+                utils.delete_lio_device(self.loop_devs[i])
+            except RuntimeError:
+                # just move on, we can do no better here
+                pass
+            os.unlink(self.dev_files[i])
+
+        self.dev_files.clear()
+        self.loop_devs.clear()
+
     def setro(self, device):
         ret, _out, _err = utils.run_command("blockdev --setro %s" % device)
         if ret != 0:
@@ -161,14 +161,14 @@ class FSTestCase(FSNoDevTestCase):
 
     def _destroy_lvm(self, vgname):
         utils.run("vgremove --yes %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % vgname)
-        utils.run("pvremove --yes %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_dev)
+        utils.run("pvremove --yes %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_devs[0])
 
     def _setup_lvm(self, vgname, lvname, lvsize="50M"):
-        ret, _out, err = utils.run_command("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\"" % self.loop_dev)
+        ret, _out, err = utils.run_command("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\"" % self.loop_devs[0])
         if ret != 0:
             raise RuntimeError("Failed to create PV for fs tests: %s" % err)
 
-        ret, _out, err = utils.run_command("vgcreate -s10M %s %s --config \"devices {use_devicesfile = 0}\"" % (vgname, self.loop_dev))
+        ret, _out, err = utils.run_command("vgcreate -s10M %s %s --config \"devices {use_devicesfile = 0}\"" % (vgname, self.loop_devs[0]))
         if ret != 0:
             raise RuntimeError("Failed to create VG for fs tests: %s" % err)
         self.addCleanup(self._destroy_lvm, vgname)
diff -pruN 3.3.1-3/tests/fs_tests/generic_test.py 3.4.0-1/tests/fs_tests/generic_test.py
--- 3.3.1-3/tests/fs_tests/generic_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/generic_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -47,78 +47,78 @@ class TestGenericWipe(GenericTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_wipe("/non/existing/device", True)
 
-        ret = utils.run("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_dev)
+        ret = utils.run("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_devs[0])
         self.assertEqual(ret, 0)
 
-        succ = BlockDev.fs_wipe(self.loop_dev, True)
+        succ = BlockDev.fs_wipe(self.loop_devs[0], True)
         self.assertTrue(succ)
 
         # now test the same multiple times in a row
         for i in range(10):
-            ret = utils.run("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_dev)
+            ret = utils.run("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_devs[0])
             self.assertEqual(ret, 0)
 
-            succ = BlockDev.fs_wipe(self.loop_dev, True)
+            succ = BlockDev.fs_wipe(self.loop_devs[0], True)
             self.assertTrue(succ)
 
         # vfat has multiple signatures on the device so it allows us to test the
         # 'all' argument of fs_wipe()
         if self._vfat_version >= Version("4.2"):
-            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1 --mbr=n" % self.loop_dev)
+            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1 --mbr=n" % self.loop_devs[0])
         else:
-            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1" % self.loop_dev)
+            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1" % self.loop_devs[0])
         self.assertEqual(ret, 0)
 
         time.sleep(0.5)
-        succ = BlockDev.fs_wipe(self.loop_dev, False)
+        succ = BlockDev.fs_wipe(self.loop_devs[0], False)
         self.assertTrue(succ)
 
         # the second signature should still be there
         # XXX: lsblk uses the udev db so it we need to make sure it is up to date
         utils.run("udevadm settle")
-        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"vfat")
 
         # get rid of all the remaining signatures (there could be vfat + PMBR for some reason)
-        succ = BlockDev.fs_wipe(self.loop_dev, True)
+        succ = BlockDev.fs_wipe(self.loop_devs[0], True)
         self.assertTrue(succ)
 
         utils.run("udevadm settle")
-        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"")
 
         # now do the wipe all in a one step
         if self._vfat_version >= Version("4.2"):
-            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1 --mbr=n" % self.loop_dev)
+            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1 --mbr=n" % self.loop_devs[0])
         else:
-            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1" % self.loop_dev)
+            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1" % self.loop_devs[0])
         self.assertEqual(ret, 0)
 
-        succ = BlockDev.fs_wipe(self.loop_dev, True)
+        succ = BlockDev.fs_wipe(self.loop_devs[0], True)
         self.assertTrue(succ)
 
         utils.run("udevadm settle")
-        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"")
 
         # try to wipe empty device
         with self.assertRaisesRegex(GLib.GError, "No signature detected on the device"):
-            BlockDev.fs_wipe(self.loop_dev, True)
+            BlockDev.fs_wipe(self.loop_devs[0], True)
 
     @tag_test(TestTags.CORE)
     def test_generic_wipe_force(self):
-        ret = utils.run("mkfs.ext2 %s >/dev/null 2>&1" % self.loop_dev)
+        ret = utils.run("mkfs.ext2 %s >/dev/null 2>&1" % self.loop_devs[0])
         self.assertEqual(ret, 0)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             # default should be force=False
             with self.assertRaisesRegex(GLib.GError, "Failed to open the device"):
-                BlockDev.fs_wipe(self.loop_dev)
+                BlockDev.fs_wipe(self.loop_devs[0])
 
-            succ = BlockDev.fs_wipe(self.loop_dev, force=True)
+            succ = BlockDev.fs_wipe(self.loop_devs[0], force=True)
             self.assertTrue(succ)
 
-        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"")
 
 
@@ -130,50 +130,50 @@ class TestClean(GenericTestCase):
             BlockDev.fs_clean("/non/existing/device")
 
         # empty device shouldn't fail
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
         self.assertTrue(succ)
 
-        ret = utils.run("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_dev)
+        ret = utils.run("pvcreate -ff -y %s --config \"devices {use_devicesfile = 0}\" >/dev/null 2>&1" % self.loop_devs[0])
         self.assertEqual(ret, 0)
 
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
         self.assertTrue(succ)
 
         # XXX: lsblk uses the udev db so it we need to make sure it is up to date
         utils.run("udevadm settle")
-        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"")
 
         # vfat has multiple signatures on the device so it allows us to test
         # that clean removes all signatures
         if self._vfat_version >= Version("4.2"):
-            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1 --mbr=n" % self.loop_dev)
+            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1 --mbr=n" % self.loop_devs[0])
         else:
-            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1" % self.loop_dev)
+            ret = utils.run("mkfs.vfat -I %s >/dev/null 2>&1" % self.loop_devs[0])
         self.assertEqual(ret, 0)
 
         time.sleep(0.5)
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
         self.assertTrue(succ)
 
         utils.run("udevadm settle")
-        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"")
 
     @tag_test(TestTags.CORE)
     def test_generic_clean_force(self):
-        ret = utils.run("mkfs.ext2 %s >/dev/null 2>&1" % self.loop_dev)
+        ret = utils.run("mkfs.ext2 %s >/dev/null 2>&1" % self.loop_devs[0])
         self.assertEqual(ret, 0)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             # default should be force=False
             with self.assertRaisesRegex(GLib.GError, "Failed to open the device"):
-                BlockDev.fs_clean(self.loop_dev)
+                BlockDev.fs_clean(self.loop_devs[0])
 
-            succ = BlockDev.fs_clean(self.loop_dev, force=True)
+            succ = BlockDev.fs_clean(self.loop_devs[0], force=True)
             self.assertTrue(succ)
 
-        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sTYPE", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"")
 
 
@@ -366,7 +366,7 @@ class GenericMkfs(GenericTestCase):
 
     def _test_ext_generic_mkfs(self, fsname, info_fn, label=None, uuid=None, force=False, extra=None, default_label=None):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
         self.assertTrue(succ)
 
         supported, flags, _util = BlockDev.fs_can_mkfs(fsname)
@@ -376,21 +376,21 @@ class GenericMkfs(GenericTestCase):
         if flags & BlockDev.FSMkfsOptionsFlags.DRY_RUN:
             # try dry run first
             options = BlockDev.FSMkfsOptions(None, None, True, False)
-            succ = BlockDev.fs_mkfs(self.loop_dev, fsname, options)
+            succ = BlockDev.fs_mkfs(self.loop_devs[0], fsname, options)
             self.assertTrue(succ)
 
-            fstype = BlockDev.fs_get_fstype (self.loop_dev)
+            fstype = BlockDev.fs_get_fstype (self.loop_devs[0])
             self.assertIsNone(fstype)
 
         options = BlockDev.FSMkfsOptions(label, uuid, False, False)
 
-        succ = BlockDev.fs_mkfs(self.loop_dev, fsname, options, extra)
+        succ = BlockDev.fs_mkfs(self.loop_devs[0], fsname, options, extra)
         self.assertTrue(succ)
 
-        fstype = BlockDev.fs_get_fstype (self.loop_dev)
+        fstype = BlockDev.fs_get_fstype (self.loop_devs[0])
         self.assertEqual(fstype, fsname)
 
-        info = info_fn(self.loop_dev)
+        info = info_fn(self.loop_devs[0])
         self.assertIsNotNone(info)
         if label is not None:
             if label == "":
@@ -406,11 +406,11 @@ class GenericMkfs(GenericTestCase):
         if force:
             # try overwriting the existing filesystem, should fail without force
             with self.assertRaises(GLib.GError):
-                BlockDev.fs_mkfs(self.loop_dev, fsname)
+                BlockDev.fs_mkfs(self.loop_devs[0], fsname)
 
             # now add the option
             options = BlockDev.FSMkfsOptions(force=True)
-            succ = BlockDev.fs_mkfs(self.loop_dev, fsname, options, extra)
+            succ = BlockDev.fs_mkfs(self.loop_devs[0], fsname, options, extra)
             self.assertTrue(succ)
 
     def test_exfat_generic_mkfs(self):
@@ -427,7 +427,7 @@ class GenericMkfs(GenericTestCase):
         self._test_ext_generic_mkfs("ext2", BlockDev.fs_ext2_get_info, label, uuid, False)
 
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
         self.assertTrue(succ)
 
         options = BlockDev.FSMkfsOptions(label, uuid, False, False)
@@ -435,20 +435,20 @@ class GenericMkfs(GenericTestCase):
         # and try with a custom extra arg (we can get block size from the info)
         extra = BlockDev.ExtraArg("-b", "4096")
 
-        succ = BlockDev.fs_mkfs(self.loop_dev, "ext2", options, [extra])
+        succ = BlockDev.fs_mkfs(self.loop_devs[0], "ext2", options, [extra])
         self.assertTrue(succ)
 
-        fstype = BlockDev.fs_get_fstype (self.loop_dev)
+        fstype = BlockDev.fs_get_fstype (self.loop_devs[0])
         self.assertEqual(fstype, "ext2")
 
-        info = BlockDev.fs_ext2_get_info(self.loop_dev)
+        info = BlockDev.fs_ext2_get_info(self.loop_devs[0])
         self.assertEqual(info.label, label)
         self.assertEqual(info.uuid, uuid)
         self.assertEqual(info.block_size, 4096)
 
         # try with -F, it doesn't really do anything with stdin closed so just a sanity check
         options = BlockDev.FSMkfsOptions(force=True)
-        succ = BlockDev.fs_mkfs(self.loop_dev, "ext2", options)
+        succ = BlockDev.fs_mkfs(self.loop_devs[0], "ext2", options)
         self.assertTrue(succ)
 
     def test_ext3_generic_mkfs(self):
@@ -525,14 +525,14 @@ class GenericMkfs(GenericTestCase):
 
         options = BlockDev.FSMkfsOptions(no_pt=True)
 
-        succ = BlockDev.fs_mkfs(self.loop_dev, "vfat", options, None)
+        succ = BlockDev.fs_mkfs(self.loop_devs[0], "vfat", options, None)
         self.assertTrue(succ)
 
-        fstype = BlockDev.fs_get_fstype (self.loop_dev)
+        fstype = BlockDev.fs_get_fstype (self.loop_devs[0])
         self.assertEqual(fstype, "vfat")
 
         # there should be no partition
-        self.assertFalse(os.path.exists(self.loop_dev + "1"))
+        self.assertFalse(os.path.exists(self.loop_devs[0] + "1"))
 
     def test_xfs_generic_mkfs(self):
         """ Test generic mkfs with XFS """
@@ -583,10 +583,10 @@ class GenericMkfs(GenericTestCase):
 
     def test_generic_mkfs_no_options(self):
         """ Test that fs_mkfs works without options specified """
-        succ = BlockDev.fs_mkfs(self.loop_dev, "ext2")
+        succ = BlockDev.fs_mkfs(self.loop_devs[0], "ext2")
         self.assertTrue(succ)
 
-        info = BlockDev.fs_ext2_get_info(self.loop_dev)
+        info = BlockDev.fs_ext2_get_info(self.loop_devs[0])
         self.assertIsNotNone(info)
         self.assertFalse(info.label)  # label should be empty by default
 
@@ -594,7 +594,7 @@ class GenericMkfs(GenericTestCase):
         """ Test that generic mkfs fails correctly with unknown/unsupported filesystem """
 
         with self.assertRaisesRegex(GLib.GError, "Filesystem 'non-existing-fs' is not supported"):
-            BlockDev.fs_mkfs(self.loop_dev, "non-existing-fs")
+            BlockDev.fs_mkfs(self.loop_devs[0], "non-existing-fs")
 
     def test_can_mkfs(self):
         """ Test checking whether mkfs is supported """
@@ -614,18 +614,18 @@ class GenericCheck(GenericTestCase):
 
     def _test_generic_check(self, mkfs_function, fstype=None):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         self.log = []
         # check for consistency (expected to be ok)
-        succ = BlockDev.fs_check(self.loop_dev)
+        succ = BlockDev.fs_check(self.loop_devs[0])
         self.assertTrue(succ)
 
         if fstype:
-            succ = BlockDev.fs_check(self.loop_dev, fstype)
+            succ = BlockDev.fs_check(self.loop_devs[0], fstype)
             self.assertTrue(succ)
 
     def _my_progress_func(self, task, status, completion, msg):
@@ -702,17 +702,17 @@ class GenericCheck(GenericTestCase):
 class GenericRepair(GenericTestCase):
     def _test_generic_repair(self, mkfs_function, fstype):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # repair (expected to succeed)
-        succ = BlockDev.fs_repair(self.loop_dev)
+        succ = BlockDev.fs_repair(self.loop_devs[0])
         self.assertTrue(succ)
 
         # repair (expected to succeed)
-        succ = BlockDev.fs_repair(self.loop_dev, fstype)
+        succ = BlockDev.fs_repair(self.loop_devs[0], fstype)
         self.assertTrue(succ)
 
     def test_ext4_generic_repair(self):
@@ -759,20 +759,20 @@ class GenericRepair(GenericTestCase):
 class GenericSetLabel(GenericTestCase):
     def _test_generic_set_label(self, mkfs_function, fstype):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         succ = BlockDev.fs_check_label(fstype, "new_label")
         self.assertTrue(succ)
 
         # set label (expected to succeed)
-        succ = BlockDev.fs_set_label(self.loop_dev, "new_label")
+        succ = BlockDev.fs_set_label(self.loop_devs[0], "new_label")
         self.assertTrue(succ)
 
         # set label (expected to succeed)
-        succ = BlockDev.fs_set_label(self.loop_dev, "new_label", fstype)
+        succ = BlockDev.fs_set_label(self.loop_devs[0], "new_label", fstype)
         self.assertTrue(succ)
 
     def test_ext4_generic_set_label(self):
@@ -825,23 +825,23 @@ class GenericSetLabel(GenericTestCase):
 class GenericSetUUID(GenericTestCase):
     def _test_generic_set_uuid(self, mkfs_function, fstype, test_uuid="4d7086c4-a4d3-432f-819e-73da03870df9", expected_uuid=None):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # set uuid (expected to succeed)
-        succ = BlockDev.fs_set_uuid(self.loop_dev, test_uuid)
+        succ = BlockDev.fs_set_uuid(self.loop_devs[0], test_uuid)
         self.assertTrue(succ)
 
-        fs_uuid = check_output(["blkid", "-ovalue", "-sUUID", "-p", self.loop_dev]).decode().strip()
+        fs_uuid = check_output(["blkid", "-ovalue", "-sUUID", "-p", self.loop_devs[0]]).decode().strip()
         if expected_uuid:
             self.assertEqual(fs_uuid, expected_uuid)
         else:
             self.assertEqual(fs_uuid, test_uuid)
 
         # set empty/random UUID
-        succ = BlockDev.fs_set_uuid(self.loop_dev, None, fstype)
+        succ = BlockDev.fs_set_uuid(self.loop_devs[0], None, fstype)
         self.assertTrue(succ)
 
         # check uuid format
@@ -913,22 +913,22 @@ class GenericSetUUID(GenericTestCase):
 class GenericResize(GenericTestCase):
     def _test_generic_resize(self, mkfs_function, fstype, size_delta=0, min_size=130*1024**2):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
-        size = BlockDev.fs_get_size(self.loop_dev)
+        size = BlockDev.fs_get_size(self.loop_devs[0])
 
         # shrink
-        succ = BlockDev.fs_resize(self.loop_dev, min_size)
+        succ = BlockDev.fs_resize(self.loop_devs[0], min_size)
         self.assertTrue(succ)
-        new_size = BlockDev.fs_get_size(self.loop_dev)
+        new_size = BlockDev.fs_get_size(self.loop_devs[0])
         self.assertAlmostEqual(new_size, min_size, delta=size_delta)
 
         # resize to maximum size
-        succ = BlockDev.fs_resize(self.loop_dev, 0, fstype)
+        succ = BlockDev.fs_resize(self.loop_devs[0], 0, fstype)
         self.assertTrue(succ)
-        new_size = BlockDev.fs_get_size(self.loop_dev)
+        new_size = BlockDev.fs_get_size(self.loop_devs[0])
         # should be back to original size
         self.assertAlmostEqual(new_size, size, delta=size_delta)
 
@@ -959,23 +959,23 @@ class GenericResize(GenericTestCase):
             return fi.size + 4096
 
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_prepare(self.loop_dev)
+        succ = mkfs_prepare(self.loop_devs[0])
         self.assertTrue(succ)
 
-        size = ntfs_size(self.loop_dev)
+        size = ntfs_size(self.loop_devs[0])
 
         # shrink
-        succ = BlockDev.fs_resize(self.loop_dev, 80 * 1024**2)
+        succ = BlockDev.fs_resize(self.loop_devs[0], 80 * 1024**2)
         self.assertTrue(succ)
-        new_size = ntfs_size(self.loop_dev)
+        new_size = ntfs_size(self.loop_devs[0])
         self.assertEqual(new_size, 80 * 1024**2)
 
         # resize to maximum size
-        succ = BlockDev.fs_resize(self.loop_dev, 0, "ntfs")
+        succ = BlockDev.fs_resize(self.loop_devs[0], 0, "ntfs")
         self.assertTrue(succ)
-        new_size = ntfs_size(self.loop_dev)
+        new_size = ntfs_size(self.loop_devs[0])
         # should be back to original size
         self.assertEqual(new_size, size)
 
@@ -1087,14 +1087,14 @@ class GenericResize(GenericTestCase):
             self.skipTest("skipping exFAT: not available")
 
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = BlockDev.fs_exfat_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_exfat_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # no resize support for exFAT
         with self.assertRaisesRegex(GLib.GError, "Resizing filesystem 'exfat' is not supported."):
-            BlockDev.fs_resize(self.loop_dev, 80 * 1024**2)
+            BlockDev.fs_resize(self.loop_devs[0], 80 * 1024**2)
 
     def test_btrfs_generic_resize(self):
         """Test generic resize function with an btrfs file system"""
@@ -1114,23 +1114,23 @@ class GenericResize(GenericTestCase):
             self.skipTest("skipping Btrfs: not available")
 
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = BlockDev.fs_btrfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_btrfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
-        size = BlockDev.fs_get_size(self.loop_dev)
+        size = BlockDev.fs_get_size(self.loop_devs[0])
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             # shrink
-            succ = BlockDev.fs_resize(self.loop_dev, 300*1024**2)
+            succ = BlockDev.fs_resize(self.loop_devs[0], 300*1024**2)
             self.assertTrue(succ)
-            new_size = BlockDev.fs_get_size(self.loop_dev)
+            new_size = BlockDev.fs_get_size(self.loop_devs[0])
             self.assertAlmostEqual(new_size, 300*1024**2)
 
             # resize to maximum size
-            succ = BlockDev.fs_resize(self.loop_dev, 0, "btrfs")
+            succ = BlockDev.fs_resize(self.loop_devs[0], 0, "btrfs")
             self.assertTrue(succ)
-            new_size = BlockDev.fs_get_size(self.loop_dev)
+            new_size = BlockDev.fs_get_size(self.loop_devs[0])
             # should be back to original size
             self.assertAlmostEqual(new_size, size)
 
@@ -1140,30 +1140,30 @@ class GenericResize(GenericTestCase):
             self.skipTest("skipping UDF: not available")
 
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = BlockDev.fs_udf_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_udf_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # no resize support for UDF
         with self.assertRaisesRegex(GLib.GError, "Resizing filesystem 'udf' is not supported."):
-            BlockDev.fs_resize(self.loop_dev, 80 * 1024**2)
+            BlockDev.fs_resize(self.loop_devs[0], 80 * 1024**2)
 
 
 class GenericGetFreeSpace(GenericTestCase):
     def _test_get_free_space(self, mkfs_function, fstype, size_delta=0):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
-        size = BlockDev.fs_get_size(self.loop_dev)
-        free = BlockDev.fs_get_free_space(self.loop_dev)
+        size = BlockDev.fs_get_size(self.loop_devs[0])
+        free = BlockDev.fs_get_free_space(self.loop_devs[0])
         self.assertNotEqual(free, 0)
         self.assertLessEqual(free, size)
 
-        size = BlockDev.fs_get_size(self.loop_dev, fstype)
-        free = BlockDev.fs_get_free_space(self.loop_dev, fstype)
+        size = BlockDev.fs_get_size(self.loop_devs[0], fstype)
+        free = BlockDev.fs_get_free_space(self.loop_devs[0], fstype)
         self.assertNotEqual(free, 0)
         self.assertLessEqual(free, size)
 
@@ -1216,25 +1216,25 @@ class GenericGetFreeSpace(GenericTestCas
             self.skipTest("skipping UDF: not available")
 
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_udf_mkfs(self.loop_dev)
+        succ = BlockDev.fs_udf_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
         with self.assertRaisesRegex(GLib.GError, "Getting free space on filesystem 'udf' is not supported."):
-            BlockDev.fs_get_free_space(self.loop_dev)
+            BlockDev.fs_get_free_space(self.loop_devs[0])
 
 
 class GenericGetMinSize(GenericTestCase):
     def _test_get_min_size(self, mkfs_function, fstype):
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
 
-        succ = mkfs_function(self.loop_dev, None)
+        succ = mkfs_function(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        size = BlockDev.fs_get_min_size(self.loop_dev)
+        size = BlockDev.fs_get_min_size(self.loop_devs[0])
         self.assertNotEqual(size, 0)
 
     def test_ext2_test_get_min_size(self):
@@ -1258,32 +1258,32 @@ class GenericGetMinSize(GenericTestCase)
     def test_xfs_get_get_min_size(self):
         """Test generic min size function with a xfs file system"""
         # clean the device
-        succ = BlockDev.fs_clean(self.loop_dev)
+        succ = BlockDev.fs_clean(self.loop_devs[0])
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
         with self.assertRaisesRegex(GLib.GError, "Getting minimum size of filesystem 'xfs' is not supported."):
-            BlockDev.fs_get_min_size(self.loop_dev)
+            BlockDev.fs_get_min_size(self.loop_devs[0])
 
 
 class FSFreezeTest(GenericTestCase):
 
     def _clean_up(self):
         try:
-            BlockDev.fs_unfreeze(self.loop_dev)
+            BlockDev.fs_unfreeze(self.loop_devs[0])
         except:
             pass
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
         super(FSFreezeTest, self)._clean_up()
 
     def test_freeze_xfs(self):
         """ Test basic freezing and un-freezing with XFS """
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # try to freeze with non-existing mountpoint
@@ -1293,8 +1293,8 @@ class FSFreezeTest(GenericTestCase):
         tmp = tempfile.mkdtemp(prefix="libblockdev.", suffix="freeze_test")
         self.addCleanup(os.rmdir, tmp)
 
-        self.addCleanup(utils.umount, self.loop_dev)
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, "xfs", None)
+        self.addCleanup(utils.umount, self.loop_devs[0])
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, "xfs", None)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
@@ -1312,14 +1312,14 @@ class FSFreezeTest(GenericTestCase):
     def test_freeze_vfat(self):
         """ Test basic freezing and un-freezing with FAT """
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         tmp = tempfile.mkdtemp(prefix="libblockdev.", suffix="freeze_test")
         self.addCleanup(os.rmdir, tmp)
 
-        self.addCleanup(utils.umount, self.loop_dev)
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, "vfat", None)
+        self.addCleanup(utils.umount, self.loop_devs[0])
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, "vfat", None)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
diff -pruN 3.3.1-3/tests/fs_tests/mount_test.py 3.4.0-1/tests/fs_tests/mount_test.py
--- 3.3.1-3/tests/fs_tests/mount_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/mount_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -38,40 +38,40 @@ class MountTestCase(FSTestCase):
     def test_mount(self):
         """ Test basic mounting and unmounting """
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         tmp = tempfile.mkdtemp(prefix="libblockdev.", suffix="mount_test")
         self.addCleanup(os.rmdir, tmp)
 
-        self.addCleanup(utils.umount, self.loop_dev)
+        self.addCleanup(utils.umount, self.loop_devs[0])
 
         # try mounting unknown filesystem type
         with self.assertRaisesRegex(GLib.GError, r"Filesystem type .* not configured in kernel."):
-            BlockDev.fs_mount(self.loop_dev, tmp, "nonexisting", None)
+            BlockDev.fs_mount(self.loop_devs[0], tmp, "nonexisting", None)
 
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, "vfat", None)
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, "vfat", None)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
         succ = BlockDev.fs_is_mountpoint(tmp)
         self.assertTrue(succ)
 
-        mnt = BlockDev.fs_get_mountpoint(self.loop_dev)
+        mnt = BlockDev.fs_get_mountpoint(self.loop_devs[0])
         self.assertEqual(mnt, tmp)
 
-        succ = BlockDev.fs_unmount(self.loop_dev, False, False, None)
+        succ = BlockDev.fs_unmount(self.loop_devs[0], False, False, None)
         self.assertTrue(succ)
         self.assertFalse(os.path.ismount(tmp))
 
         succ = BlockDev.fs_is_mountpoint(tmp)
         self.assertFalse(succ)
 
-        mnt = BlockDev.fs_get_mountpoint(self.loop_dev)
+        mnt = BlockDev.fs_get_mountpoint(self.loop_devs[0])
         self.assertIsNone(mnt)
 
         # mount again to test unmount using the mountpoint
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, None, None)
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, None, None)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
@@ -80,26 +80,26 @@ class MountTestCase(FSTestCase):
         self.assertFalse(os.path.ismount(tmp))
 
         # mount with some options
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, "vfat", "ro,noexec")
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, "vfat", "ro,noexec")
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
         _ret, out, _err = utils.run_command("grep %s /proc/mounts" % tmp)
         self.assertTrue(out)
         self.assertIn("ro,noexec", out)
 
-        succ = BlockDev.fs_unmount(self.loop_dev, False, False, None)
+        succ = BlockDev.fs_unmount(self.loop_devs[0], False, False, None)
         self.assertTrue(succ)
         self.assertFalse(os.path.ismount(tmp))
 
         # mount with UID=0 and GUID=0
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, run_as_uid="0", run_as_gid="0")
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, run_as_uid="0", run_as_gid="0")
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
         with self.assertRaises(GLib.GError):
-            BlockDev.fs_mount(self.loop_dev, tmp, run_as_uid="a", run_as_gid="a")
+            BlockDev.fs_mount(self.loop_devs[0], tmp, run_as_uid="a", run_as_gid="a")
 
-        succ = BlockDev.fs_unmount(self.loop_dev, False, False, None)
+        succ = BlockDev.fs_unmount(self.loop_devs[0], False, False, None)
         self.assertTrue(succ)
         self.assertFalse(os.path.ismount(tmp))
 
@@ -147,26 +147,26 @@ class MountTestCase(FSTestCase):
         fstab = utils.read_file("/etc/fstab")
         self.addCleanup(utils.write_file, "/etc/fstab", fstab)
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         tmp = tempfile.mkdtemp(prefix="libblockdev.", suffix="mount_fstab_test")
         self.addCleanup(os.rmdir, tmp)
 
-        utils.write_file("/etc/fstab", "%s %s vfat defaults 0 0\n" % (self.loop_dev, tmp))
+        utils.write_file("/etc/fstab", "%s %s vfat defaults 0 0\n" % (self.loop_devs[0], tmp))
 
         # try to mount and unmount using the device
-        self.addCleanup(utils.umount, self.loop_dev)
-        succ = BlockDev.fs_mount(device=self.loop_dev)
+        self.addCleanup(utils.umount, self.loop_devs[0])
+        succ = BlockDev.fs_mount(device=self.loop_devs[0])
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
-        succ = BlockDev.fs_unmount(self.loop_dev)
+        succ = BlockDev.fs_unmount(self.loop_devs[0])
         self.assertTrue(succ)
         self.assertFalse(os.path.ismount(tmp))
 
         # try to mount and unmount just using the mountpoint
-        self.addCleanup(utils.umount, self.loop_dev)
+        self.addCleanup(utils.umount, self.loop_devs[0])
         succ = BlockDev.fs_mount(mountpoint=tmp)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
@@ -182,44 +182,44 @@ class MountTestCase(FSTestCase):
         fstab = utils.read_file("/etc/fstab")
         self.addCleanup(utils.write_file, "/etc/fstab", fstab)
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         tmp = tempfile.mkdtemp(prefix="libblockdev.", suffix="mount_fstab_user_test")
         self.addCleanup(os.rmdir, tmp)
 
-        utils.write_file("/etc/fstab", "%s %s vfat defaults,users 0 0\n" % (self.loop_dev, tmp))
+        utils.write_file("/etc/fstab", "%s %s vfat defaults,users 0 0\n" % (self.loop_devs[0], tmp))
 
         uid, gid = self._add_user()
         self.addCleanup(self._remove_user)
 
         # try to mount and unmount the device as the user
-        self.addCleanup(utils.umount, self.loop_dev)
-        succ = BlockDev.fs_mount(device=self.loop_dev, run_as_uid=uid, run_as_gid=gid)
+        self.addCleanup(utils.umount, self.loop_devs[0])
+        succ = BlockDev.fs_mount(device=self.loop_devs[0], run_as_uid=uid, run_as_gid=gid)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
-        succ = BlockDev.fs_unmount(self.loop_dev, run_as_uid=uid, run_as_gid=gid)
+        succ = BlockDev.fs_unmount(self.loop_devs[0], run_as_uid=uid, run_as_gid=gid)
         self.assertTrue(succ)
         self.assertFalse(os.path.ismount(tmp))
 
         # remove the 'users' option
-        utils.write_file("/etc/fstab", "%s %s vfat defaults 0 0\n" % (self.loop_dev, tmp))
+        utils.write_file("/etc/fstab", "%s %s vfat defaults 0 0\n" % (self.loop_devs[0], tmp))
 
         # try to mount and unmount the device as the user --> should fail now
         with self.assertRaises(GLib.GError):
-            BlockDev.fs_mount(device=self.loop_dev, run_as_uid=uid, run_as_gid=gid)
+            BlockDev.fs_mount(device=self.loop_devs[0], run_as_uid=uid, run_as_gid=gid)
 
         self.assertFalse(os.path.ismount(tmp))
 
         # now mount as root to test unmounting
-        self.addCleanup(utils.umount, self.loop_dev)
-        succ = BlockDev.fs_mount(device=self.loop_dev)
+        self.addCleanup(utils.umount, self.loop_devs[0])
+        succ = BlockDev.fs_mount(device=self.loop_devs[0])
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
         with self.assertRaises(GLib.GError):
-            BlockDev.fs_unmount(self.loop_dev, run_as_uid=uid, run_as_gid=gid)
+            BlockDev.fs_unmount(self.loop_devs[0], run_as_uid=uid, run_as_gid=gid)
         self.assertTrue(os.path.ismount(tmp))
 
     def test_mount_nilfs(self):
@@ -230,27 +230,27 @@ class MountTestCase(FSTestCase):
         if not self.nilfs2_avail:
             self.skipTest("skipping NILFS mount test: not available")
 
-        succ = BlockDev.fs_nilfs2_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_nilfs2_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         tmp = tempfile.mkdtemp(prefix="libblockdev.", suffix="mount_test")
         self.addCleanup(os.rmdir, tmp)
 
-        self.addCleanup(utils.umount, self.loop_dev)
+        self.addCleanup(utils.umount, self.loop_devs[0])
 
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, "nilfs2", None)
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, "nilfs2", None)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
-        succ = BlockDev.fs_unmount(self.loop_dev, False, False, None)
+        succ = BlockDev.fs_unmount(self.loop_devs[0], False, False, None)
         self.assertTrue(succ)
         self.assertFalse(os.path.ismount(tmp))
 
-        mnt = BlockDev.fs_get_mountpoint(self.loop_dev)
+        mnt = BlockDev.fs_get_mountpoint(self.loop_devs[0])
         self.assertIsNone(mnt)
 
         # mount again to test unmount using the mountpoint
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, None, None)
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, None, None)
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
 
@@ -259,13 +259,13 @@ class MountTestCase(FSTestCase):
         self.assertFalse(os.path.ismount(tmp))
 
         # mount with some options
-        succ = BlockDev.fs_mount(self.loop_dev, tmp, "nilfs2", "ro")
+        succ = BlockDev.fs_mount(self.loop_devs[0], tmp, "nilfs2", "ro")
         self.assertTrue(succ)
         self.assertTrue(os.path.ismount(tmp))
         _ret, out, _err = utils.run_command("grep %s /proc/mounts" % tmp)
         self.assertTrue(out)
         self.assertIn("ro", out)
 
-        succ = BlockDev.fs_unmount(self.loop_dev, False, False, None)
+        succ = BlockDev.fs_unmount(self.loop_devs[0], False, False, None)
         self.assertTrue(succ)
         self.assertFalse(os.path.ismount(tmp))
diff -pruN 3.3.1-3/tests/fs_tests/nilfs_test.py 3.4.0-1/tests/fs_tests/nilfs_test.py
--- 3.3.1-3/tests/fs_tests/nilfs_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/nilfs_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -101,18 +101,18 @@ class NILFS2TestMkfs(NILFS2TestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_nilfs2_mkfs("/non/existing/device", None)
 
-        succ = BlockDev.fs_nilfs2_mkfs(self.loop_dev)
+        succ = BlockDev.fs_nilfs2_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "nilfs2")
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
 
 class NILFS2MkfsWithLabel(NILFS2TestCase):
@@ -120,10 +120,10 @@ class NILFS2MkfsWithLabel(NILFS2TestCase
         """Verify that it is possible to create an nilfs2 file system with label"""
 
         ea = BlockDev.ExtraArg.new("-L", "test_label")
-        succ = BlockDev.fs_nilfs2_mkfs(self.loop_dev, [ea])
+        succ = BlockDev.fs_nilfs2_mkfs(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label")
 
@@ -132,10 +132,10 @@ class NILFS2GetInfo(NILFS2TestCase):
     def test_nilfs2_get_info(self):
         """Verify that it is possible to get info about an nilfs2 file system"""
 
-        succ = BlockDev.fs_nilfs2_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_nilfs2_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
         # should be an non-empty string
@@ -149,28 +149,28 @@ class NILFS2SetLabel(NILFS2TestCase):
     def test_nilfs2_set_label(self):
         """Verify that it is possible to set label of an nilfs2 file system"""
 
-        succ = BlockDev.fs_nilfs2_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_nilfs2_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
-        succ = BlockDev.fs_nilfs2_set_label(self.loop_dev, "test_label")
+        succ = BlockDev.fs_nilfs2_set_label(self.loop_devs[0], "test_label")
         self.assertTrue(succ)
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label")
 
-        succ = BlockDev.fs_nilfs2_set_label(self.loop_dev, "test_label2")
+        succ = BlockDev.fs_nilfs2_set_label(self.loop_devs[0], "test_label2")
         self.assertTrue(succ)
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label2")
 
-        succ = BlockDev.fs_nilfs2_set_label(self.loop_dev, "")
+        succ = BlockDev.fs_nilfs2_set_label(self.loop_devs[0], "")
         self.assertTrue(succ)
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
@@ -185,27 +185,27 @@ class NILFS2Resize(NILFS2TestCase):
     def test_nilfs2_resize(self):
         """Verify that it is possible to resize an nilfs2 file system"""
 
-        succ = BlockDev.fs_nilfs2_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_nilfs2_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         with self.assertRaisesRegex(GLib.GError, "is not currently mounted"):
-            BlockDev.fs_nilfs2_resize(self.loop_dev, 100 * 1024**2)
+            BlockDev.fs_nilfs2_resize(self.loop_devs[0], 100 * 1024**2)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             # shrink
-            succ = BlockDev.fs_nilfs2_resize(self.loop_dev, 100 * 1024**2)
+            succ = BlockDev.fs_nilfs2_resize(self.loop_devs[0], 100 * 1024**2)
             self.assertTrue(succ)
 
             # grow
-            succ = BlockDev.fs_nilfs2_resize(self.loop_dev, 120 * 1024**2)
+            succ = BlockDev.fs_nilfs2_resize(self.loop_devs[0], 120 * 1024**2)
             self.assertTrue(succ)
 
             # shrink again
-            succ = BlockDev.fs_nilfs2_resize(self.loop_dev, 100 * 1024**2)
+            succ = BlockDev.fs_nilfs2_resize(self.loop_devs[0], 100 * 1024**2)
             self.assertTrue(succ)
 
             # resize to maximum size
-            succ = BlockDev.fs_nilfs2_resize(self.loop_dev, 0)
+            succ = BlockDev.fs_nilfs2_resize(self.loop_devs[0], 0)
             self.assertTrue(succ)
 
 
@@ -216,19 +216,19 @@ class NILFS2SetUUID(NILFS2TestCase):
     def test_nilfs2_set_uuid(self):
         """Verify that it is possible to set UUID of an nilfs2 file system"""
 
-        succ = BlockDev.fs_nilfs2_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_nilfs2_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_nilfs2_set_uuid(self.loop_dev, self.test_uuid)
+        succ = BlockDev.fs_nilfs2_set_uuid(self.loop_devs[0], self.test_uuid)
         self.assertTrue(succ)
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, self.test_uuid)
 
         # no uuid -> random
-        succ = BlockDev.fs_nilfs2_set_uuid(self.loop_dev, None)
+        succ = BlockDev.fs_nilfs2_set_uuid(self.loop_devs[0], None)
         self.assertTrue(succ)
-        fi = BlockDev.fs_nilfs2_get_info(self.loop_dev)
+        fi = BlockDev.fs_nilfs2_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         self.assertNotEqual(fi.uuid, self.test_uuid)
diff -pruN 3.3.1-3/tests/fs_tests/ntfs_test.py 3.4.0-1/tests/fs_tests/ntfs_test.py
--- 3.3.1-3/tests/fs_tests/ntfs_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/ntfs_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -109,18 +109,18 @@ class NTFSTestMkfs(NTFSTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_ntfs_mkfs("/non/existing/device", None)
 
-        succ = BlockDev.fs_ntfs_mkfs(self.loop_dev)
+        succ = BlockDev.fs_ntfs_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "ntfs")
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
 
 class NTFSMkfsWithLabel(NTFSTestCase):
@@ -128,10 +128,10 @@ class NTFSMkfsWithLabel(NTFSTestCase):
         """Verify that it is possible to create an NTFS file system with label"""
 
         ea = BlockDev.ExtraArg.new("-L", "test_label")
-        succ = BlockDev.fs_ntfs_mkfs(self.loop_dev, [ea])
+        succ = BlockDev.fs_ntfs_mkfs(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label")
 
@@ -140,10 +140,10 @@ class NTFSGetInfo(NTFSTestCase):
     def test_ntfs_get_info(self):
         """Verify that it is possible to get info about an NTFS file system"""
 
-        succ = BlockDev.fs_ntfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_ntfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
         # should be an non-empty string
@@ -156,58 +156,58 @@ class NTFSResize(NTFSTestCase):
     def test_ntfs_resize(self):
         """Verify that it is possible to resize an NTFS file system"""
 
-        succ = BlockDev.fs_ntfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_ntfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_ntfs_repair(self.loop_dev)
+        succ = BlockDev.fs_ntfs_repair(self.loop_devs[0])
         self.assertTrue(succ)
 
         # shrink
-        succ = BlockDev.fs_ntfs_resize(self.loop_dev, 80 * 1024**2)
+        succ = BlockDev.fs_ntfs_resize(self.loop_devs[0], 80 * 1024**2)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_ntfs_repair(self.loop_dev)
+        succ = BlockDev.fs_ntfs_repair(self.loop_devs[0])
         self.assertTrue(succ)
 
         # resize to maximum size
-        succ = BlockDev.fs_ntfs_resize(self.loop_dev, 0)
+        succ = BlockDev.fs_ntfs_resize(self.loop_devs[0], 0)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_ntfs_repair(self.loop_dev)
+        succ = BlockDev.fs_ntfs_repair(self.loop_devs[0])
         self.assertTrue(succ)
 
         # get min size and resize to it
-        size = BlockDev.fs_ntfs_get_min_size(self.loop_dev)
+        size = BlockDev.fs_ntfs_get_min_size(self.loop_devs[0])
         self.assertNotEqual(size, 0)
 
-        succ = BlockDev.fs_ntfs_resize(self.loop_dev, size)
+        succ = BlockDev.fs_ntfs_resize(self.loop_devs[0], size)
         self.assertTrue(succ)
 
 
 class NTFSSetLabel(NTFSTestCase):
     def test_ntfs_set_label(self):
-        succ = BlockDev.fs_ntfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_ntfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
-        succ = BlockDev.fs_ntfs_set_label(self.loop_dev, "TEST_LABEL")
+        succ = BlockDev.fs_ntfs_set_label(self.loop_devs[0], "TEST_LABEL")
         self.assertTrue(succ)
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL")
 
-        succ = BlockDev.fs_ntfs_set_label(self.loop_dev, "TEST_LABEL2")
+        succ = BlockDev.fs_ntfs_set_label(self.loop_devs[0], "TEST_LABEL2")
         self.assertTrue(succ)
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL2")
 
-        succ = BlockDev.fs_ntfs_set_label(self.loop_dev, "")
+        succ = BlockDev.fs_ntfs_set_label(self.loop_devs[0], "")
         self.assertTrue(succ)
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
@@ -224,19 +224,19 @@ class NTFSSetUUID(NTFSTestCase):
 
     def test_ntfs_set_uuid(self):
         """Verify that it is possible to set UUID of an ntfs file system"""
-        succ = BlockDev.fs_ntfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_ntfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_ntfs_set_uuid(self.loop_dev, self.test_uuid)
+        succ = BlockDev.fs_ntfs_set_uuid(self.loop_devs[0], self.test_uuid)
         self.assertTrue(succ)
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, self.test_uuid)
 
         # no uuid -> random
-        succ = BlockDev.fs_ntfs_set_uuid(self.loop_dev, None)
+        succ = BlockDev.fs_ntfs_set_uuid(self.loop_devs[0], None)
         self.assertTrue(succ)
-        fi = BlockDev.fs_ntfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_ntfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         self.assertNotEqual(fi.uuid, self.test_uuid)
diff -pruN 3.3.1-3/tests/fs_tests/udf_test.py 3.4.0-1/tests/fs_tests/udf_test.py
--- 3.3.1-3/tests/fs_tests/udf_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/udf_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -61,33 +61,33 @@ class UdfTestMkfs(UdfTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_udf_mkfs("/non/existing/device", None)
 
-        succ = BlockDev.fs_udf_mkfs(self.loop_dev)
+        succ = BlockDev.fs_udf_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "udf")
 
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.revision, "2.01")
         self.assertEqual(fi.block_size, 512)
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
         # now try with custom revision and media type
-        succ = BlockDev.fs_udf_mkfs(self.loop_dev, "bdr", "2.50", 4096)
+        succ = BlockDev.fs_udf_mkfs(self.loop_devs[0], "bdr", "2.50", 4096)
         self.assertTrue(succ)
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "udf")
 
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.revision, "2.50")
         self.assertEqual(fi.block_size, 4096)
@@ -97,10 +97,10 @@ class UdfGetInfo(UdfTestCase):
     def test_udf_get_info(self):
         """Verify that it is possible to get info about an udf file system"""
 
-        succ = BlockDev.fs_udf_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_udf_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "LinuxUDF")
         self.assertEqual(fi.vid, "LinuxUDF")
@@ -117,58 +117,58 @@ class UdfSetLabel(UdfTestCase):
     def test_udf_set_label(self):
         """Verify that it is possible to set label of an udf file system"""
 
-        succ = BlockDev.fs_udf_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_udf_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "LinuxUDF")
 
-        succ = BlockDev.fs_udf_set_label(self.loop_dev, "test_label")
+        succ = BlockDev.fs_udf_set_label(self.loop_devs[0], "test_label")
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "test_label")
         self.assertEqual(fi.vid, "test_label")
         self.assertEqual(fi.lvid, "test_label")
 
         # longer label -- vid should be truncated to 30
-        succ = BlockDev.fs_udf_set_label(self.loop_dev, "a" * 126)
+        succ = BlockDev.fs_udf_set_label(self.loop_devs[0], "a" * 126)
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "a" * 126)
         self.assertEqual(fi.vid, "a" * 30)
         self.assertEqual(fi.lvid, "a" * 126)
 
-        succ = BlockDev.fs_udf_set_label(self.loop_dev, "ä" * 126)
+        succ = BlockDev.fs_udf_set_label(self.loop_devs[0], "ä" * 126)
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "ä" * 126)
         self.assertEqual(fi.vid, "ä" * 30)
         self.assertEqual(fi.lvid, "ä" * 126)
 
         # with unicode -- vid should be truncated to 15 or 30 based on position
-        succ = BlockDev.fs_udf_set_label(self.loop_dev, "ř" * 63)
+        succ = BlockDev.fs_udf_set_label(self.loop_devs[0], "ř" * 63)
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "ř" * 63)
         self.assertEqual(fi.vid, "ř" * 15)
         self.assertEqual(fi.lvid, "ř" * 63)
 
-        succ = BlockDev.fs_udf_set_label(self.loop_dev, "ř" + "a" * 62)
+        succ = BlockDev.fs_udf_set_label(self.loop_devs[0], "ř" + "a" * 62)
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "ř" + "a" * 62)
         self.assertEqual(fi.vid, "ř" + "a" * 14)
         self.assertEqual(fi.lvid, "ř" + "a" * 62)
 
-        succ = BlockDev.fs_udf_set_label(self.loop_dev, "a" * 62 + "ř")
+        succ = BlockDev.fs_udf_set_label(self.loop_devs[0], "a" * 62 + "ř")
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "a" * 62 + "ř")
         self.assertEqual(fi.vid, "a" * 30)
@@ -202,19 +202,19 @@ class UdfSetUUID(UdfTestCase):
         if not self.udf_avail:
             self.skipTest("skipping UDF: not available")
 
-        succ = BlockDev.fs_udf_mkfs(self.loop_dev)
+        succ = BlockDev.fs_udf_mkfs(self.loop_devs[0])
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_udf_set_uuid(self.loop_dev, self.test_uuid)
+        succ = BlockDev.fs_udf_set_uuid(self.loop_devs[0], self.test_uuid)
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, self.test_uuid)
 
         # no uuid -> random
-        succ = BlockDev.fs_udf_set_uuid(self.loop_dev, None)
+        succ = BlockDev.fs_udf_set_uuid(self.loop_devs[0], None)
         self.assertTrue(succ)
-        fi = BlockDev.fs_udf_get_info(self.loop_dev)
+        fi = BlockDev.fs_udf_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         self.assertNotEqual(fi.uuid, self.test_uuid)
diff -pruN 3.3.1-3/tests/fs_tests/vfat_test.py 3.4.0-1/tests/fs_tests/vfat_test.py
--- 3.3.1-3/tests/fs_tests/vfat_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/vfat_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -128,18 +128,18 @@ class VfatTestMkfs(VfatTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_vfat_mkfs("/non/existing/device", self._mkfs_options)
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, self._mkfs_options)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], self._mkfs_options)
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "vfat")
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
 
 class VfatMkfsWithLabel(VfatTestCase):
@@ -148,12 +148,12 @@ class VfatMkfsWithLabel(VfatTestCase):
 
         ea = BlockDev.ExtraArg.new("-n", "TEST_LABEL")
         if self._mkfs_options:
-            succ = BlockDev.fs_vfat_mkfs(self.loop_dev, [ea] + self._mkfs_options)
+            succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], [ea] + self._mkfs_options)
         else:
-            succ = BlockDev.fs_vfat_mkfs(self.loop_dev, [ea])
+            succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], [ea])
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL")
 
@@ -162,13 +162,13 @@ class VfatTestCheck(VfatTestCase):
     def test_vfat_check(self):
         """Verify that it is possible to check an vfat file system"""
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, self._mkfs_options)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], self._mkfs_options)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_vfat_check(self.loop_dev, None)
+        succ = BlockDev.fs_vfat_check(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_vfat_check(self.loop_dev, None)
+        succ = BlockDev.fs_vfat_check(self.loop_devs[0], None)
         self.assertTrue(succ)
 
 
@@ -176,10 +176,10 @@ class VfatTestRepair(VfatTestCase):
     def test_vfat_repair(self):
         """Verify that it is possible to repair an vfat file system"""
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, self._mkfs_options)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], self._mkfs_options)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_vfat_repair(self.loop_dev, None)
+        succ = BlockDev.fs_vfat_repair(self.loop_devs[0], None)
         self.assertTrue(succ)
 
 
@@ -187,10 +187,10 @@ class VfatGetInfo(VfatTestCase):
     def test_vfat_get_info(self):
         """Verify that it is possible to get info about an vfat file system"""
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, self._mkfs_options)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], self._mkfs_options)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
         # should be an non-empty string
@@ -201,28 +201,28 @@ class VfatSetLabel(VfatTestCase):
     def test_vfat_set_label(self):
         """Verify that it is possible to set label of an vfat file system"""
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, self._mkfs_options)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], self._mkfs_options)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
-        succ = BlockDev.fs_vfat_set_label(self.loop_dev, "TEST_LABEL")
+        succ = BlockDev.fs_vfat_set_label(self.loop_devs[0], "TEST_LABEL")
         self.assertTrue(succ)
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL")
 
-        succ = BlockDev.fs_vfat_set_label(self.loop_dev, "TEST_LABEL2")
+        succ = BlockDev.fs_vfat_set_label(self.loop_devs[0], "TEST_LABEL2")
         self.assertTrue(succ)
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL2")
 
-        succ = BlockDev.fs_vfat_set_label(self.loop_dev, "")
+        succ = BlockDev.fs_vfat_set_label(self.loop_devs[0], "")
         self.assertTrue(succ)
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
@@ -240,25 +240,25 @@ class VfatSetUUID(VfatTestCase):
         if DOSFSTOOLS_VERSION <= Version("4.1"):
             self.skipTest("dosfstools >= 4.2 needed to set UUID")
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, self._mkfs_options)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], self._mkfs_options)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_vfat_set_uuid(self.loop_dev, "2E24EC82")
+        succ = BlockDev.fs_vfat_set_uuid(self.loop_devs[0], "2E24EC82")
         self.assertTrue(succ)
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, "2E24-EC82")
 
         # should be also support with the dash
-        succ = BlockDev.fs_vfat_set_uuid(self.loop_dev, "2E24-EC82")
+        succ = BlockDev.fs_vfat_set_uuid(self.loop_devs[0], "2E24-EC82")
         self.assertTrue(succ)
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, "2E24-EC82")
 
-        succ = BlockDev.fs_vfat_set_uuid(self.loop_dev, "")
+        succ = BlockDev.fs_vfat_set_uuid(self.loop_devs[0], "")
         self.assertTrue(succ)
-        fi = BlockDev.fs_vfat_get_info(self.loop_dev)
+        fi = BlockDev.fs_vfat_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertTrue(fi.uuid)  # new random, not empty
         self.assertNotEqual(fi.uuid, "2E24-EC82")
@@ -287,21 +287,21 @@ class VfatResize(VfatTestCase):
     def test_vfat_resize(self):
         """Verify that it is possible to resize an vfat file system"""
 
-        succ = BlockDev.fs_vfat_mkfs(self.loop_dev, self._mkfs_options)
+        succ = BlockDev.fs_vfat_mkfs(self.loop_devs[0], self._mkfs_options)
         self.assertTrue(succ)
 
         # shrink
-        succ = BlockDev.fs_vfat_resize(self.loop_dev, 130 * 1024**2)
+        succ = BlockDev.fs_vfat_resize(self.loop_devs[0], 130 * 1024**2)
         self.assertTrue(succ)
 
         # grow
-        succ = BlockDev.fs_vfat_resize(self.loop_dev, 140 * 1024**2)
+        succ = BlockDev.fs_vfat_resize(self.loop_devs[0], 140 * 1024**2)
         self.assertTrue(succ)
 
         # shrink again
-        succ = BlockDev.fs_vfat_resize(self.loop_dev, 130 * 1024**2)
+        succ = BlockDev.fs_vfat_resize(self.loop_devs[0], 130 * 1024**2)
         self.assertTrue(succ)
 
         # resize to maximum size
-        succ = BlockDev.fs_vfat_resize(self.loop_dev, 0)
+        succ = BlockDev.fs_vfat_resize(self.loop_devs[0], 0)
         self.assertTrue(succ)
diff -pruN 3.3.1-3/tests/fs_tests/xfs_test.py 3.4.0-1/tests/fs_tests/xfs_test.py
--- 3.3.1-3/tests/fs_tests/xfs_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/fs_tests/xfs_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -112,37 +112,37 @@ class XfsTestMkfs(XfsTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.fs_xfs_mkfs("/non/existing/device", None)
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
         # just try if we can mount the file system
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             pass
 
         # check the fstype
-        fstype = BlockDev.fs_get_fstype(self.loop_dev)
+        fstype = BlockDev.fs_get_fstype(self.loop_devs[0])
         self.assertEqual(fstype, "xfs")
 
-        BlockDev.fs_wipe(self.loop_dev, True)
+        BlockDev.fs_wipe(self.loop_devs[0], True)
 
 
 class XfsTestCheck(XfsTestCase):
     def test_xfs_check(self):
         """Verify that it is possible to check an xfs file system"""
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_xfs_check(self.loop_dev)
+        succ = BlockDev.fs_xfs_check(self.loop_devs[0])
         self.assertTrue(succ)
 
         # mounted RO, can be checked and nothing happened in/to the file system,
         # so it should be just reported as clean
-        with mounted(self.loop_dev, self.mount_dir, ro=True):
-            succ = BlockDev.fs_xfs_check(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir, ro=True):
+            succ = BlockDev.fs_xfs_check(self.loop_devs[0])
             self.assertTrue(succ)
 
-        succ = BlockDev.fs_xfs_check(self.loop_dev)
+        succ = BlockDev.fs_xfs_check(self.loop_devs[0])
         self.assertTrue(succ)
 
 
@@ -150,17 +150,17 @@ class XfsTestRepair(XfsTestCase):
     def test_xfs_repair(self):
         """Verify that it is possible to repair an xfs file system"""
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_xfs_repair(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_repair(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        with mounted(self.loop_dev, self.mount_dir):
+        with mounted(self.loop_devs[0], self.mount_dir):
             with self.assertRaises(GLib.GError):
-                BlockDev.fs_xfs_repair(self.loop_dev, None)
+                BlockDev.fs_xfs_repair(self.loop_devs[0], None)
 
-        succ = BlockDev.fs_xfs_repair(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_repair(self.loop_devs[0], None)
         self.assertTrue(succ)
 
 
@@ -169,10 +169,10 @@ class XfsGetInfo(XfsTestCase):
     def test_xfs_get_info(self):
         """Verify that it is possible to get info about an xfs file system"""
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertEqual(fi.block_size, 4096)
         self.assertEqual(fi.block_count, self.loop_size / 4096)
         self.assertEqual(fi.label, "")
@@ -184,32 +184,32 @@ class XfsSetLabel(XfsTestCase):
     def test_xfs_set_label(self):
         """Verify that it is possible to set label of an xfs file system"""
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
-        succ = BlockDev.fs_xfs_set_label(self.loop_dev, "TEST_LABEL")
+        succ = BlockDev.fs_xfs_set_label(self.loop_devs[0], "TEST_LABEL")
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL")
 
-        succ = BlockDev.fs_xfs_set_label(self.loop_dev, "TEST_LABEL2")
+        succ = BlockDev.fs_xfs_set_label(self.loop_devs[0], "TEST_LABEL2")
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "TEST_LABEL2")
 
-        succ = BlockDev.fs_xfs_set_label(self.loop_dev, "")
+        succ = BlockDev.fs_xfs_set_label(self.loop_devs[0], "")
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.label, "")
 
@@ -291,36 +291,36 @@ class XfsSetUUID(XfsTestCase):
     def test_xfs_set_uuid(self):
         """Verify that it is possible to set UUID of an xfs file system"""
 
-        succ = BlockDev.fs_xfs_mkfs(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_mkfs(self.loop_devs[0], None)
         self.assertTrue(succ)
 
-        succ = BlockDev.fs_xfs_set_uuid(self.loop_dev, self.test_uuid)
+        succ = BlockDev.fs_xfs_set_uuid(self.loop_devs[0], self.test_uuid)
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertEqual(fi.uuid, self.test_uuid)
 
-        succ = BlockDev.fs_xfs_set_uuid(self.loop_dev, "nil")
+        succ = BlockDev.fs_xfs_set_uuid(self.loop_devs[0], "nil")
         self.assertTrue(succ)
 
         # can't use libblockdev because XFS without UUID can't be mounted
-        fs_type = check_output(["blkid", "-ovalue", "-sUUID", "-p", self.loop_dev]).strip()
+        fs_type = check_output(["blkid", "-ovalue", "-sUUID", "-p", self.loop_devs[0]]).strip()
         self.assertEqual(fs_type, b"")
 
-        succ = BlockDev.fs_xfs_set_uuid(self.loop_dev, "generate")
+        succ = BlockDev.fs_xfs_set_uuid(self.loop_devs[0], "generate")
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         random_uuid = fi.uuid
 
         # no uuid -> random
-        succ = BlockDev.fs_xfs_set_uuid(self.loop_dev, None)
+        succ = BlockDev.fs_xfs_set_uuid(self.loop_devs[0], None)
         self.assertTrue(succ)
-        with mounted(self.loop_dev, self.mount_dir):
-            fi = BlockDev.fs_xfs_get_info(self.loop_dev)
+        with mounted(self.loop_devs[0], self.mount_dir):
+            fi = BlockDev.fs_xfs_get_info(self.loop_devs[0])
         self.assertTrue(fi)
         self.assertNotEqual(fi.uuid, "")
         self.assertNotEqual(fi.uuid, random_uuid)
diff -pruN 3.3.1-3/tests/library_test.py 3.4.0-1/tests/library_test.py
--- 3.3.1-3/tests/library_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/library_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -164,8 +164,8 @@ class PluginsTestCase(unittest.TestCase)
 
     def _clean_up(self):
         # change the sources back and recompile
-        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm.c > /dev/null")
-        os.system("make -C src/plugins/ libbd_lvm.la >/dev/null 2>&1")
+        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm/lvm-common.c > /dev/null")
+        os.system("make -C src/plugins/lvm/ libbd_lvm.la >/dev/null 2>&1")
 
         os.environ["LIBBLOCKDEV_CONFIG_DIR"] = self.orig_config_dir
 
@@ -182,8 +182,8 @@ class PluginsTestCase(unittest.TestCase)
         self.assertNotEqual(orig_max_size, 1024)
 
         # change the sources and recompile
-        os.system("sed -ri 's?MAX_LV_SIZE;?1024;//test-change?' src/plugins/lvm.c > /dev/null")
-        ret = os.system("make -C src/plugins/ libbd_lvm.la >/dev/null 2>&1")
+        os.system("sed -ri 's?MAX_LV_SIZE;?1024;//test-change?' src/plugins/lvm/lvm-common.c > /dev/null")
+        ret = os.system("make -C src/plugins/lvm/ libbd_lvm.la >/dev/null 2>&1")
         self.assertEqual(ret, 0, "Failed to recompile libblockdev for reload test")
 
         # library should successfully reinitialize without reloading plugins
@@ -199,8 +199,8 @@ class PluginsTestCase(unittest.TestCase)
         self.assertEqual(BlockDev.lvm_get_max_lv_size(), 1024)
 
         # change the sources back and recompile
-        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm.c > /dev/null")
-        os.system("make -C src/plugins/ libbd_lvm.la >/dev/null 2>&1")
+        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm/lvm-common.c > /dev/null")
+        os.system("make -C src/plugins/lvm/ libbd_lvm.la >/dev/null 2>&1")
 
         # library should successfully reinitialize reloading original plugins
         self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
@@ -221,16 +221,16 @@ class PluginsTestCase(unittest.TestCase)
         self.assertNotEqual(orig_max_size, 1024)
 
         # change the sources and recompile
-        os.system("sed -ri 's?MAX_LV_SIZE;?1024;//test-change?' src/plugins/lvm.c > /dev/null")
-        ret = os.system("make -C src/plugins/ libbd_lvm.la >/dev/null 2>&1")
+        os.system("sed -ri 's?MAX_LV_SIZE;?1024;//test-change?' src/plugins/lvm/lvm-common.c > /dev/null")
+        ret = os.system("make -C src/plugins/lvm/ libbd_lvm.la >/dev/null 2>&1")
         self.assertEqual(ret, 0, "Failed to recompile libblockdev for force plugin test")
 
         # proclaim the new build a different plugin
-        os.system("cp src/plugins/.libs/libbd_lvm.so src/plugins/.libs/libbd_lvm2.so")
+        os.system("cp src/plugins/lvm/.libs/libbd_lvm.so src/plugins/lvm/.libs/libbd_lvm2.so")
 
         # change the sources back and recompile
-        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm.c > /dev/null")
-        ret = os.system("make -C src/plugins/ libbd_lvm.la >/dev/null 2>&1")
+        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm/lvm-common.c > /dev/null")
+        ret = os.system("make -C src/plugins/lvm/ libbd_lvm.la >/dev/null 2>&1")
         self.assertEqual(ret, 0, "Failed to recompile libblockdev for force plugin test")
 
         # force the new plugin to be used
@@ -241,7 +241,7 @@ class PluginsTestCase(unittest.TestCase)
         self.assertEqual(BlockDev.lvm_get_max_lv_size(), 1024)
 
         # clean after ourselves
-        os.system ("rm -f src/plugins/.libs/libbd_lvm2.so")
+        os.system ("rm -f src/plugins/lvm/.libs/libbd_lvm2.so")
 
         # force the old plugin to be used
         ps = BlockDev.PluginSpec(name=BlockDev.Plugin.LVM, so_name="libbd_lvm.so")
@@ -262,16 +262,16 @@ class PluginsTestCase(unittest.TestCase)
         self.assertNotEqual(orig_max_size, 1024)
 
         # change the sources and recompile
-        os.system("sed -ri 's?MAX_LV_SIZE;?1024;//test-change?' src/plugins/lvm.c > /dev/null")
-        ret = os.system("make -C src/plugins/ libbd_lvm.la >/dev/null 2>&1")
+        os.system("sed -ri 's?MAX_LV_SIZE;?1024;//test-change?' src/plugins/lvm/lvm-common.c > /dev/null")
+        ret = os.system("make -C src/plugins/lvm/ libbd_lvm.la >/dev/null 2>&1")
         self.assertEqual(ret, 0, "Failed to recompile libblockdev for plugin priority test")
 
         # proclaim the new build a different plugin
-        os.system("cp src/plugins/.libs/libbd_lvm.so src/plugins/.libs/libbd_lvm2.so.3")
+        os.system("cp src/plugins/lvm/.libs/libbd_lvm.so src/plugins/lvm/.libs/libbd_lvm2.so.3")
 
         # change the sources back and recompile
-        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm.c > /dev/null")
-        ret = os.system("make -C src/plugins/ libbd_lvm.la >/dev/null 2>&1")
+        os.system("sed -ri 's?1024;//test-change?MAX_LV_SIZE;?' src/plugins/lvm/lvm-common.c > /dev/null")
+        ret = os.system("make -C src/plugins/lvm/ libbd_lvm.la >/dev/null 2>&1")
         self.assertEqual(ret, 0, "Failed to recompile libblockdev for plugin priority test")
 
         # now reinit the library with the config preferring the new build
@@ -312,4 +312,4 @@ class PluginsTestCase(unittest.TestCase)
         self.assertEqual(BlockDev.lvm_get_max_lv_size(), orig_max_size)
 
         # clean after ourselves
-        os.system ("rm -f src/plugins/.libs/libbd_lvm2.so")
+        os.system ("rm -f src/plugins/lvm/.libs/libbd_lvm2.so")
diff -pruN 3.3.1-3/tests/loop_test.py 3.4.0-1/tests/loop_test.py
--- 3.3.1-3/tests/loop_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/loop_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -1,18 +1,17 @@
 import os
 import unittest
-import time
 import overrides_hack
 
-from utils import create_sparse_tempfile, TestTags, tag_test, required_plugins
+from utils import create_sparse_tempfile, create_sparse_file, TestTags, tag_test, required_plugins
 
 import gi
-gi.require_version('GLib', '2.0')
 gi.require_version('BlockDev', '3.0')
-from gi.repository import GLib, BlockDev
+from gi.repository import BlockDev
 
 
 @required_plugins(("loop",))
 class LoopTestCase(unittest.TestCase):
+    _loop_size = 100 * 1024**2
 
     requested_plugins = BlockDev.plugin_specs_from_names(("loop",))
 
@@ -25,7 +24,7 @@ class LoopTestCase(unittest.TestCase):
 
     def setUp(self):
         self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("loop_test", 1024**3)
+        self.dev_file = create_sparse_tempfile("loop_test", self._loop_size)
         self.loop = None
 
     def _clean_up(self):
@@ -35,10 +34,14 @@ class LoopTestCase(unittest.TestCase):
             pass
         os.unlink(self.dev_file)
 
+    def _get_loop_size(self):
+        with open("/sys/block/%s/size" % self.loop, "r") as f:
+            return int(f.read()) * 512
+
 class LoopPluginVersionCase(LoopTestCase):
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.LOOP), "libbd_loop.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.LOOP), "libbd_loop.so.3")
 
 
 class LoopTestSetupBasic(LoopTestCase):
@@ -77,9 +80,7 @@ class LoopTestSetupOffset(LoopTestCase):
         self.assertEqual(info.offset, 10 * 1024**2)
 
         # should have smaller size due to the offset
-        with open("/sys/block/%s/size" % self.loop, "r") as f:
-            size = int(f.read()) * 512
-        self.assertEqual(size, 1024**3 - 10 * 1024 **2)
+        self.assertEqual(self._get_loop_size(), self._loop_size - 10 * 1024 **2)
 
         succ = BlockDev.loop_teardown(self.loop)
         self.assertTrue(succ)
@@ -95,9 +96,7 @@ class LoopTestSetupOffsetSize(LoopTestCa
         self.assertTrue(self.loop)
 
         # should have size as specified
-        with open("/sys/block/%s/size" % self.loop, "r") as f:
-            size = int(f.read()) * 512
-        self.assertEqual(size, 50 * 1024**2)
+        self.assertEqual(self._get_loop_size(), 50 * 1024**2)
 
         succ = BlockDev.loop_teardown(self.loop)
         self.assertTrue(succ)
@@ -221,3 +220,23 @@ class LoopTestGetSetAutoclear(LoopTestCa
         info = BlockDev.loop_info(self.loop)
         self.assertIsNotNone(info)
         self.assertFalse(info.autoclear)
+
+
+class LoopTestSetCapacity(LoopTestCase):
+    def test_loop_set_capacity(self):
+        succ, self.loop = BlockDev.loop_setup(self.dev_file)
+        self.assertTrue(succ)
+        self.assertTrue(self.loop)
+        self.assertEqual(self._get_loop_size(), self._loop_size)
+
+        # enlarge the backing file
+        create_sparse_file(self.dev_file, self._loop_size * 2)
+
+        # size shouldn't change without forcing re-read
+        self.assertEqual(self._get_loop_size(), self._loop_size)
+
+        succ = BlockDev.loop_set_capacity(self.loop)
+        self.assertTrue(succ)
+
+        # now the size should be updated
+        self.assertEqual(self._get_loop_size(), self._loop_size * 2)
diff -pruN 3.3.1-3/tests/lvm_dbus_tests.py 3.4.0-1/tests/lvm_dbus_tests.py
--- 3.3.1-3/tests/lvm_dbus_tests.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/lvm_dbus_tests.py	2025-09-24 13:46:42.000000000 +0000
@@ -1,17 +1,9 @@
-from __future__ import division
 import unittest
-import os
-import math
-import overrides_hack
-import re
-import shutil
-import time
-from contextlib import contextmanager
-from packaging.version import Version
 from itertools import chain
-import sys
 
-from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, run_command, TestTags, tag_test, read_file, required_plugins
+import _lvm_cases
+
+from utils import run_command, TestTags, tag_test, required_plugins
 
 import gi
 gi.require_version('GLib', '2.0')
@@ -23,1952 +15,36 @@ sb = dbus.SystemBus()
 lvm_dbus_running = any("lvmdbus" in name for name in chain(sb.list_names(), sb.list_activatable_names()))
 
 
-@contextmanager
-def wait_for_sync(vg_name, lv_name):
-    try:
-        yield
-    finally:
-        time.sleep(2)
-        while True:
-            ret, out, _err = run_command("LC_ALL=C lvs -o copy_percent --noheadings %s/%s" % (vg_name, lv_name))
-            if ret != 0:
-                break
-            if int(float(out)) == 100:
-                break
-            time.sleep(1)
-
-
 @required_plugins(("lvm-dbus",))
-class LVMTestCase(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        if lvm_dbus_running:
-            # force the new plugin to be used
-            cls.ps = BlockDev.PluginSpec(name=BlockDev.Plugin.LVM, so_name="libbd_lvm-dbus.so.3")
-            cls.ps2 = BlockDev.PluginSpec(name=BlockDev.Plugin.LOOP)
-            if not BlockDev.is_initialized():
-                BlockDev.init([cls.ps, cls.ps2], None)
-            else:
-                BlockDev.reinit([cls.ps, cls.ps2], True, None)
-
-        try:
-            cls.devices_avail = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.DEVICES, 0)
-        except:
-            cls.devices_avail = False
-
-    @classmethod
-    def _get_lvm_version(cls):
-        _ret, out, _err = run_command("lvm version")
-        m = re.search(r"LVM version:\s+([\d\.]+)", out)
-        if not m or len(m.groups()) != 1:
-            raise RuntimeError("Failed to determine LVM version from: %s" % out)
-        return Version(m.groups()[0])
-
-    @classmethod
-    def _get_lvm_segtypes(cls):
-        _ret, out, _err = run_command("lvm segtypes")
-        return out
-
 @unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmNoDevTestCase(LVMTestCase):
+class LvmDBusTestCase(_lvm_cases.LvmTestCase):
+    test_type = "dbus"
 
     @classmethod
     def setUpClass(cls):
-        # we are checking for info log messages and default level is warning
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_INFO)
-
-        super(LvmNoDevTestCase, cls).setUpClass()
-
-    @classmethod
-    def tearDownClass(cls):
-        # reset back to default
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_WARNING)
-        BlockDev.lvm_set_global_config(None)
-
-        super(LvmNoDevTestCase, cls).tearDownClass()
-
-    def setUp(self):
-        self._log = ""
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_is_supported_pe_size(self):
-        """Verify that lvm_is_supported_pe_size works as expected"""
-
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(6 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(12 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(15 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024**3))
-
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(512))
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(4097))
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(65535))
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(32 * 1024**3))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_supported_pe_sizes(self):
-        """Verify that supported PE sizes are really supported"""
-
-        supported = BlockDev.lvm_get_supported_pe_sizes()
-
-        for size in supported:
-            self.assertTrue(BlockDev.lvm_is_supported_pe_size(size))
-
-        self.assertIn(4 * 1024, supported)
-        self.assertIn(4 * 1024 **2, supported)
-        self.assertIn(16 * 1024**3, supported)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_max_lv_size(self):
-        """Verify that max LV size is correctly determined"""
+        ps = BlockDev.PluginSpec(name=BlockDev.Plugin.LVM, so_name="libbd_lvm-dbus.so.3")
+        cls.requested_plugins = [ps]
 
-        if os.uname()[-1] == "i686":
-            # 32-bit arch
-            expected = 16 * 1024**4
+        if not BlockDev.is_initialized():
+            BlockDev.init(cls.requested_plugins, None)
         else:
-            # 64-bit arch
-            expected = 8 * 1024**6
-
-        self.assertEqual(BlockDev.lvm_get_max_lv_size(), expected)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_round_size_to_pe(self):
-        """Verify that round_size_to_pe works as expected"""
-
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 4 * 1024**2, True), 12 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 4 * 1024**2, False), 8 * 1024**2)
-
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 6 * 1024**2, True), 12 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 6 * 1024**2, False), 6 * 1024**2)
-
-        # default PE size is 4 MiB
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 0, True), 12 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 0, False), 8 * 1024**2)
-
-        # cannot round up to GLib.MAXUINT64, but can round up over GLib.MAXUINT64 (should round down in that case)
-        biggest_multiple = (GLib.MAXUINT64 // (4 * 1024**2)) * (4 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple + (2 * 1024**2), 4 * 1024**2, True),
-                         biggest_multiple)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple + (2 * 1024**2), 4 * 1024**2, False),
-                         biggest_multiple)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple - (2 * 4 * 1024**2) + 1, 4 * 1024**2, True),
-                         biggest_multiple - (4 * 1024**2))
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple - (2 * 4 * 1024**2) + 1, 4 * 1024**2, False),
-                         biggest_multiple - (2 * 4 * 1024**2))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_lv_physical_size(self):
-        """Verify that get_lv_physical_size works as expected"""
-
-        self.assertEqual(BlockDev.lvm_get_lv_physical_size(25 * 1024**3, 4 * 1024**2),
-                         25 * 1024**3)
-
-        # default PE size is 4 MiB
-        self.assertEqual(BlockDev.lvm_get_lv_physical_size(25 * 1024**3, 0),
-                         25 * 1024**3)
-
-        self.assertEqual(BlockDev.lvm_get_lv_physical_size(11 * 1024**2, 4 * 1024**2),
-                         12 * 1024**2)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_thpool_padding(self):
-        """Verify that get_thpool_padding works as expected"""
-
-        expected_padding = BlockDev.lvm_round_size_to_pe(int(math.ceil(11 * 1024**2 * 0.2)),
-                                                         4 * 1024**2, True)
-        self.assertEqual(BlockDev.lvm_get_thpool_padding(11 * 1024**2, 4 * 1024**2, False),
-                         expected_padding)
-
-        expected_padding = BlockDev.lvm_round_size_to_pe(int(math.ceil(11 * 1024**2 * (1.0/6.0))),
-                                                         4 * 1024**2, True)
-        self.assertEqual(BlockDev.lvm_get_thpool_padding(11 * 1024**2, 4 * 1024**2, True),
-                         expected_padding)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_thpool_meta_size(self):
-        """Verify that getting recommended thin pool metadata size works as expected"""
-
-        # metadata size is calculated as 64 * pool_size / chunk_size
-        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 64 * 1024), 1 * 1024**3)
-
-        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 128 * 1024),  512 * 1024**2)
-
-        # lower limit is 4 MiB
-        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(100 * 1024**2, 128 * 1024),
-                         4 * 1024**2)
-
-        # upper limit is ~15.81 GiB
-        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(100 * 1024**4, 64 * 1024),
-                         16978542592)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_is_valid_thpool_md_size(self):
-        """Verify that is_valid_thpool_md_size works as expected"""
-
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(4 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(5 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(15 * 1024**3))
-
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(1 * 1024**2))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(3 * 1024**2))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(16 * 1024**3))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(32 * 1024**3))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_is_valid_thpool_chunk_size(self):
-        """Verify that is_valid_thpool_chunk_size works as expected"""
-
-        # 64 KiB is OK with or without discard
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(64 * 1024, True))
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(64 * 1024, False))
-
-        # 192 KiB is OK without discard, but NOK with discard
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(192 * 1024, False))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(192 * 1024, True))
-
-        # 191 KiB is NOK in both cases
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(191 * 1024, False))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(191 * 1024, True))
-
-    def _store_log(self, lvl, msg):
-        self._log += str((lvl, msg))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_set_global_config(self):
-        """Verify that getting and setting global config works as expected"""
-
-        # setup logging
-        self.assertTrue(BlockDev.reinit([self.ps], False, self._store_log))
-
-        # no global config set initially
-        self.assertEqual(BlockDev.lvm_get_global_config(), "")
-
-        # make sure we don't leave the config in some problematic shape
-        self.addCleanup(BlockDev.lvm_set_global_config, None)
-
-        # set and try to get back
-        succ = BlockDev.lvm_set_global_config("bla")
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
-
-        # reset and try to get back
-        succ = BlockDev.lvm_set_global_config(None)
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_global_config(), "")
-
-        # set twice and try to get back twice
-        succ = BlockDev.lvm_set_global_config("foo")
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_set_global_config("bla")
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
-        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
-
-        # set something sane and check it's really used
-        succ = BlockDev.lvm_set_global_config("backup {backup=0 archive=0}")
-        BlockDev.lvm_pvscan(None, False, None)
-        self.assertIn("'--config'", self._log)
-        self.assertIn("'backup {backup=0 archive=0}'", self._log)
-
-        # reset back to default
-        succ = BlockDev.lvm_set_global_config(None)
-        self.assertTrue(succ)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_set_global_devices_filter(self):
-        """Verify that getting and setting LVM devices filter works as expected"""
-        if not self.devices_avail:
-            self.skipTest("skipping LVM devices filter test: not supported")
-
-        # setup logging
-        self.assertTrue(BlockDev.reinit([self.ps], False, self._store_log))
-
-        # no global config set initially
-        self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
-
-        # set and try to get back
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
-        self.assertTrue(succ)
-        self.assertListEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sda"])
-
-        # reset and try to get back
-        succ = BlockDev.lvm_set_devices_filter(None)
-        self.assertTrue(succ)
-        self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
-
-        # set twice and try to get back twice
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sdb"])
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sdb"])
-
-        # set something sane and check it's really used
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sdb", "/dev/sdc"])
-        self.assertTrue(succ)
-        BlockDev.lvm_pvscan()
-        self.assertIn("'--devices'", self._log)
-        self.assertIn("'/dev/sdb,/dev/sdc'", self._log)
-
-        # reset back to default
-        succ = BlockDev.lvm_set_devices_filter(None)
-        self.assertTrue(succ)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_cache_get_default_md_size(self):
-        """Verify that default cache metadata size is calculated properly"""
-
-        # 1000x smaller than the data LV size, but at least 8 MiB
-        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(100 * 1024**3), (100 * 1024**3) // 1000)
-        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(80 * 1024**3), (80 * 1024**3) // 1000)
-        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(6 * 1024**3), 8 * 1024**2)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_cache_mode_bijection(self):
-        """Verify that cache modes and their string representations map to each other"""
-
-        mode_strs = {BlockDev.LVMCacheMode.WRITETHROUGH: "writethrough",
-                     BlockDev.LVMCacheMode.WRITEBACK: "writeback",
-                     BlockDev.LVMCacheMode.UNKNOWN: "unknown",
-        }
-        for mode in mode_strs.keys():
-            self.assertEqual(BlockDev.lvm_cache_get_mode_str(mode), mode_strs[mode])
-            self.assertEqual(BlockDev.lvm_cache_get_mode_from_str(mode_strs[mode]), mode)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_cache_get_mode_from_str("bla")
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_lvm_config(self):
-        """Verify that we can correctly read from LVM config"""
-
-        # should be always available, "lvmconfig" command is just alias for "lvm config"
-        succ = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.CONFIG, 0)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_config_get(None, "dir")
-
-        # get entire config
-        conf = BlockDev.lvm_config_get()
-        self.assertTrue(conf)
-        self.assertTrue(conf.startswith("config"))
-
-        # get just the "devices" section
-        conf = BlockDev.lvm_config_get("devices")
-        self.assertTrue(conf)
-        self.assertTrue(conf.startswith("devices"))
-
-        # let's be brave and assume devices/dir is set everywhere ti /dev
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full")
-        self.assertEqual(devdir, "\"/dev\"")
-
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full", values_only=False)
-        self.assertEqual(devdir, "dir=\"/dev\"")
-
-        devdir = BlockDev.lvm_config_get("devices", "dir", "default")
-        self.assertEqual(devdir, "\"/dev\"")
-
-        # let's try to override some results with --config
-        BlockDev.lvm_set_global_config("devices/dir=/test")
-
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full")
-        self.assertEqual(devdir, "\"/test\"")
-
-        # "default" config should not be affected by --config
-        devdir = BlockDev.lvm_config_get("devices", "dir", "default")
-        self.assertEqual(devdir, "\"/dev\"")
-
-        # disable global config
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full", global_config=False)
-        self.assertEqual(devdir, "\"/dev\"")
-
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVonlyTestCase(LVMTestCase):
-
-    _sparse_size = 1024**3
-
-    # :TODO:
-    #     * test pvmove (must create two PVs, a VG, a VG and some data in it
-    #       first)
-    #     * some complex test for pvs, vgs, lvs, pvinfo, vginfo and lvinfo
-    def setUp(self):
-        self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("lvm_test", self._sparse_size)
-        self.dev_file2 = create_sparse_tempfile("lvm_test", self._sparse_size)
-        self.dev_file3 = create_sparse_tempfile("lvm_test", self._sparse_size)
-        try:
-            self.loop_dev = create_lio_device(self.dev_file)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev2 = create_lio_device(self.dev_file2)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev3 = create_lio_device(self.dev_file3)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-
-    def _clean_up(self):
-        for dev in (self.loop_dev, self.loop_dev2, self.loop_dev3):
-            try:
-                BlockDev.lvm_pvremove(dev)
-            except:
-                pass
-
-            try:
-                BlockDev.lvm_devices_delete(dev)
-            except:
-                pass
-
-        try:
-            delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
-
-        try:
-            delete_lio_device(self.loop_dev2)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file2)
-
-        try:
-            delete_lio_device(self.loop_dev3)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file3)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPVcreateRemove(LvmPVonlyTestCase):
-    @tag_test(TestTags.CORE)
-    def test_pvcreate_and_pvremove(self):
-        """Verify that it's possible to create and destroy a PV"""
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_pvcreate("/non/existing/device", 0, 0, None)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-        # this time try to specify data_alignment and metadata_size
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 2*1024**2, 4*1024**2, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_pvremove("/non/existing/device", None)
-
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-        # already removed -- not an issue
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPVresize(LvmPVonlyTestCase):
-    def test_pvresize(self):
-        """Verify that it's possible to resize a PV"""
-
-        with self.assertRaises(GLib.GError):
-            succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None)
-
-        with self.assertRaises(GLib.GError):
-            succ = BlockDev.lvm_pvresize("/non/existing/device", 200 * 1024**2, None)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertEqual(info.pv_size, 200 * 1024**2)
-
-        succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**3, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertEqual(info.pv_size, 200 * 1024**3)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPVscan(LvmPVonlyTestCase):
-    def test_pvscan(self):
-        """Verify that pvscan runs without issues with cache or without"""
-
-        succ = BlockDev.lvm_pvscan(None, False, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvscan(self.loop_dev, True, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvscan(None, True, None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPVinfo(LvmPVonlyTestCase):
-    def test_pvinfo(self):
-        """Verify that it's possible to gather info about a PV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_name, self.loop_dev)
-        self.assertTrue(info.pv_uuid)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPVs(LvmPVonlyTestCase):
-    def test_pvs(self):
-        """Verify that it's possible to gather info about PVs"""
-
-        pvs = BlockDev.lvm_pvs()
-        orig_len = len(pvs)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        pvs = BlockDev.lvm_pvs()
-        self.assertGreater(len(pvs), orig_len)
-        self.assertTrue(any(info.pv_name == self.loop_dev for info in pvs))
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-
-        self.assertTrue(any(info.pv_uuid == all_info.pv_uuid for all_info in pvs))
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGTestCase(LvmPVonlyTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_vgremove("testVG", None)
-        except:
-            pass
-
-        # XXX remove lingering /dev entries
-        shutil.rmtree("/dev/testVG", ignore_errors=True)
-
-        LvmPVonlyTestCase._clean_up(self)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGcreateRemove(LvmPVVGTestCase):
-    @tag_test(TestTags.CORE)
-    def test_vgcreate_vgremove(self):
-        """Verify that it is possible to create and destroy a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgcreate("testVG", ["/non/existing/device"], 0, None)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # VG already exists
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-
-        succ = BlockDev.lvm_vgremove("testVG", None)
-        self.assertTrue(succ)
-
-        # no longer exists
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgremove("testVG", None)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGrename(LvmPVVGTestCase):
-    def test_vgrename(self):
-        """Verify that it is possible to rename a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # try rename
-        succ = BlockDev.lvm_vgrename("testVG", "testVG_new", None)
-        self.assertTrue(succ)
-
-        # rename back
-        succ = BlockDev.lvm_vgrename("testVG_new", "testVG", None)
-        self.assertTrue(succ)
-
-        # (hopefully) non-existing VG
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgrename("testVG_new", "testVG", None)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGactivateDeactivate(LvmPVVGTestCase):
-    def test_vgactivate_vgdeactivate(self):
-        """Verify that it is possible to (de)activate a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgactivate("nonexistingVG", None)
-
-        succ = BlockDev.lvm_vgactivate("testVG", None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgdeactivate("nonexistingVG", None)
-
-        succ = BlockDev.lvm_vgdeactivate("testVG", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgactivate("testVG", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgdeactivate("testVG", None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGextendReduce(LvmPVVGTestCase):
-    def test_vgextend_vgreduce(self):
-        """Verify that it is possible to extend/reduce a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgextend("nonexistingVG", self.loop_dev2, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgextend("testVG", "/non/existing/device", None)
-
-        succ = BlockDev.lvm_vgextend("testVG", self.loop_dev2, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgreduce("testVG", self.loop_dev, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgextend("testVG", self.loop_dev, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgreduce("testVG", self.loop_dev2, None)
-        self.assertTrue(succ)
-
-        # try to remove missing PVs (there are none)
-        succ = BlockDev.lvm_vgreduce("testVG", None, None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGinfo(LvmPVVGTestCase):
-    def test_vginfo(self):
-        """Verify that it is possible to gather info about a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.name, "testVG")
-        self.assertTrue(info.uuid)
-        self.assertEqual(info.pv_count, 2)
-        self.assertLess(info.size, 2 * 1024**3)
-        self.assertEqual(info.free, info.size)
-        self.assertEqual(info.extent_size, 4 * 1024**2)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGs(LvmPVVGTestCase):
-    def test_vgs(self):
-        """Verify that it's possible to gather info about VGs"""
-
-        vgs = BlockDev.lvm_vgs()
-        orig_len = len(vgs)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        vgs = BlockDev.lvm_vgs()
-        self.assertGreater(len(vgs), orig_len)
-        self.assertTrue(any(info.name == "testVG" for info in vgs))
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-
-        self.assertTrue(any(info.uuid == all_info.uuid for all_info in vgs))
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgremove("nonexistingVG", None)
-
-        succ = BlockDev.lvm_vgremove("testVG", None)
-        self.assertTrue(succ)
-
-        # already removed
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgremove("testVG", None)
-
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGLocking(LvmPVVGTestCase):
-    @tag_test(TestTags.UNSAFE)
-    def test_vglock_stop_start(self):
-        """Verify that it is possible to start and stop locking on a VG"""
-
-        # better not do anything if lvmlockd is running, shared VGs have
-        # a tendency to wreak havoc on your system if you look at them wrong
-        ret, _out, _err = run_command("systemctl is-active lvmlockd")
-        if ret == 0:
-            self.skipTest("lvmlockd is running, skipping")
-
-        _ret, out, _err = run_command("lvm config 'global/use_lvmlockd'")
-        if "use_lvmlockd=0" not in out:
-            self.skipTest("lvmlockd is enabled, skipping")
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # this actually doesn't "test" anything, the commands will just say lvmlockd is not
-        # running and return 0, but that's good enough for us
-        succ = BlockDev.lvm_vglock_start("testVG")
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vglock_stop("testVG")
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPVTags(LvmPVVGTestCase):
-    def test_pvtags(self):
-        """Verify that it's possible to set and get info about PV tags"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        # only pvs in a vg can be tagged so we need a vg here
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertFalse(info.pv_tags)
-
-        succ = BlockDev.lvm_add_pv_tags(self.loop_dev, ["a", "b", "c"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_tags, ["a", "b", "c"])
-
-        succ = BlockDev.lvm_delete_pv_tags(self.loop_dev, ["a", "b"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_tags, ["c"])
-
-        succ = BlockDev.lvm_add_pv_tags(self.loop_dev, ["e"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_tags, ["c", "e"])
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestVGTags(LvmPVVGTestCase):
-    def test_vgtags(self):
-        """Verify that it's possible to set and get info about VG tags"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
+            BlockDev.reinit(cls.requested_plugins, True, None)
 
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertFalse(info.vg_tags)
-
-        succ = BlockDev.lvm_add_vg_tags("testVG", ["a", "b", "c"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.vg_tags, ["a", "b", "c"])
-
-        succ = BlockDev.lvm_delete_vg_tags("testVG", ["a", "b"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.vg_tags, ["c"])
-
-        succ = BlockDev.lvm_add_vg_tags("testVG", ["e"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.vg_tags, ["c", "e"])
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGLVTestCase(LvmPVVGTestCase):
-    def _clean_up(self):
         try:
-            BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+            cls.devices_avail = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.DEVICES, 0)
         except:
-            pass
-
-        LvmPVVGTestCase._clean_up(self)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVcreateRemove(LvmPVVGLVTestCase):
-    @tag_test(TestTags.CORE)
-    def test_lvcreate_lvremove(self):
-        """Verify that it's possible to create/destroy an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("nonexistingVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, ["/non/existing/device"], None)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # not enough space (only one PV)
-        with self.assertRaisesRegex(GLib.GError, "Insufficient free space"):
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 1048 * 1024**2, None, [self.loop_dev], None)
-
-        # enough space (two PVs)
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 1048 * 1024**2, None, [self.loop_dev, self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("nonexistingVG", "testLV", True, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("testVG", "nonexistingLV", True, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("nonexistingVG", "nonexistingLV", True, None)
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # already removed
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVRemoveExtraArgs(LvmPVVGLVTestCase):
-    def test_lvremove_extra_args(self):
-        """Verify that specifying extra arguments for lvremove works as expected"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        # try multiple options together with --test, the LV should not be removed
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", False, [BlockDev.ExtraArg.new("--test", "")])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_name, "testLV")
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, [BlockDev.ExtraArg.new("--test", "")])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_name, "testLV")
-
-        # try to remove without --force
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", False, None)
-        self.assertTrue(succ)
-
-        # already removed
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("testVG", "testLV", True, None)
+            cls.devices_avail = False
 
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVcreateWithExtra(LvmPVVGLVTestCase):
-    def __init__(self, *args, **kwargs):
-        LvmPVVGLVTestCase.__init__(self, *args, **kwargs)
-        self.log = ""
-        self.ignore_log = True
 
+class LvmNoDevTestCase(_lvm_cases.LvmNoDevTestCase, LvmDBusTestCase):
     @classmethod
     def setUpClass(cls):
-        # we are checking for info log messages and default level is warning
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_INFO)
-
-        super(LvmTestLVcreateWithExtra, cls).setUpClass()
-
-    @classmethod
-    def tearDownClass(cls):
-        # reset back to default
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_WARNING)
-
-        super(LvmTestLVcreateWithExtra, cls).tearDownClass()
-
-    def my_log_func(self, level, msg):
-        if self.ignore_log:
-            return
-        # not much to verify here
-        self.assertTrue(isinstance(level, int))
-        self.assertTrue(isinstance(msg, str))
-
-        self.log += msg + "\n"
-
-    def test_lvcreate_with_extra(self):
-        """Verify that it's possible to create an LV with extra arguments"""
-
-        self.ignore_log = True
-        self.assertTrue(BlockDev.reinit([self.ps, self.ps2], False, self.my_log_func))
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("nonexistingVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, ["/non/existing/device"], None)
-
-        self.ignore_log = False
-        ea = BlockDev.ExtraArg.new("-Z", "y")
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], [ea])
-        self.assertTrue(succ)
-        match = re.search(r"'-Z': <'y'>", self.log)
-        self.assertIsNot(match, None)
-
-        self.assertTrue(BlockDev.reinit([self.ps, self.ps2], False, None))
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVcreateType(LvmPVVGLVTestCase):
-
-    _sparse_size = 200 * 1024**2
-
-    def test_lvcreate_type(self):
-        """Verify it's possible to create LVs with various types"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # try to create a striped LV
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "striped", [self.loop_dev, self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        # verify that the LV has the requested segtype
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.segtype, "striped")
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        with wait_for_sync("testVG", "testLV"):
-            # try to create a mirrored LV
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "mirror", [self.loop_dev, self.loop_dev2], None)
-            self.assertTrue(succ)
-
-        # verify that the LV has the requested segtype
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.segtype, "mirror")
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        with wait_for_sync("testVG", "testLV"):
-            # try to create a raid1 LV
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "raid1", [self.loop_dev, self.loop_dev2], None)
-            self.assertTrue(succ)
-
-        # verify that the LV has the requested segtype
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.segtype, "raid1")
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVactivateDeactivate(LvmPVVGLVTestCase):
-    def test_lvactivate_lvdeactivate(self):
-        """Verify it's possible to (de)actiavate an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvactivate("nonexistingVG", "testLV", True)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvactivate("testVG", "nonexistingLV", True)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvactivate("nonexistingVG", "nonexistingLV", True)
-
-        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvdeactivate("nonexistingVG", "testLV", None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvdeactivate("testVG", "nonexistingLV", None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvdeactivate("nonexistingVG", "nonexistingLV", None)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-        # try activating in shared mode, unfortunately no way to check whether it really
-        # works or not
-        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, True)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVresize(LvmPVVGLVTestCase):
-    def test_lvresize(self):
-        """Verify that it's possible to resize an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvresize("nonexistingVG", "testLV", 768 * 1024**2, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvresize("testVG", "nonexistingLV", 768 * 1024**2, None)
-
-        # grow
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 768 * 1024**2, None)
-        self.assertTrue(succ)
-
-        # same size
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvresize("testVG", "testLV", 768 * 1024**2, None)
-
-        # shrink
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 512 * 1024**2, None)
-        self.assertTrue(succ)
-
-        # shrink, not a multiple of 512
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 500 * 1024**2, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-        # try to shrink when deactivated
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 400 * 1024**2, None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVrename(LvmPVVGLVTestCase):
-    def test_lvrename(self):
-        """Verify that it's possible to rename an LV"""
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvrename("nonexistingVG", "testLV", "newTestLV", None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvrename("testVG", "nonexistingLV", "newTestLV", None)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        # rename
-        succ = BlockDev.lvm_lvrename("testVG", "testLV", "newTestLV", None)
-        self.assertTrue(succ)
-
-        # and back
-        succ = BlockDev.lvm_lvrename("testVG", "newTestLV", "testLV", None)
-        self.assertTrue(succ)
-
-        # needs a change
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvrename("testVG", "testLV", "testLV", None)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVsnapshots(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_snapshotcreate_lvorigin_snapshotmerge(self):
-        """Verify that LV snapshot support works"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvsnapshotcreate("testVG", "testLV", "testLV_bak", 256 * 1024**2, None)
-        self.assertTrue(succ)
-
-        origin_name = BlockDev.lvm_lvorigin("testVG", "testLV_bak")
-        lvi = BlockDev.lvm_lvinfo("testVG", "testLV_bak")
-        self.assertEqual(origin_name, "testLV")
-        self.assertEqual(lvi.origin, "testLV")
-        self.assertIn("snapshot", lvi.roles.split(","))
-
-        succ = BlockDev.lvm_lvsnapshotmerge("testVG", "testLV_bak", None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVinfo(LvmPVVGLVTestCase):
-    def test_lvinfo(self):
-        """Verify that it is possible to gather info about an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_name, "testLV")
-        self.assertEqual(info.vg_name, "testVG")
-        self.assertTrue(info.uuid)
-        self.assertEqual(info.size, 512 * 1024**2)
-        self.assertIn("public", info.roles.split(","))
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVs(LvmPVVGLVTestCase):
-    def test_lvs(self):
-        """Verify that it's possible to gather info about LVs"""
-
-        lvs = BlockDev.lvm_lvs(None)
-        orig_len = len(lvs)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs(None)
-        self.assertGreater(len(lvs), orig_len)
-        self.assertTrue(any(info.lv_name == "testLV" and info.vg_name == "testVG" for info in lvs))
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-
-        self.assertTrue(any(info.uuid == all_info.uuid for all_info in lvs))
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 1)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVsMultiSegment(LvmPVVGLVTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testLV2", True, None)
-        except:
-            pass
-
-        LvmPVVGLVTestCase._clean_up(self)
-
-    def test_lvinfo_tree(self):
-        """Verify that it's possible to gather info about LVs"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 10 * 1024**2)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 1)
-        self.assertListEqual([lv.lv_name for lv in lvs], ["testLV"])
-
-        # the LV will have a single segment on loop_dev
-        info = BlockDev.lvm_lvinfo_tree("testVG", "testLV")
-        self.assertEqual(info.segtype, "linear")
-        self.assertEqual(len(info.segs), 1)
-        self.assertEqual(info.segs[0].pvdev, self.loop_dev)
-
-        # add second LV
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV2", 10 * 1024**2)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 2)
-        self.assertCountEqual([lv.lv_name for lv in lvs], ["testLV", "testLV2"])
-
-        # by resizing the first LV we will create two segments
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 20 * 1024**2, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo_tree("testVG", "testLV")
-        self.assertEqual(info.segtype, "linear")
-        self.assertEqual(len(info.segs), 2)
-        self.assertEqual(info.segs[0].pvdev, self.loop_dev)
-        self.assertEqual(info.segs[1].pvdev, self.loop_dev)
-        self.assertNotEqual(info.segs[0].pv_start_pe, info.segs[1].pv_start_pe)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 2)
-        self.assertListEqual([lv.lv_name for lv in lvs], ["testLV", "testLV2"])
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGthpoolTestCase(LvmPVVGTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testPool", True, None)
-        except:
-            pass
-
-        LvmPVVGTestCase._clean_up(self)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPartialLVs(LvmPVVGLVTestCase):
-    # the mirror halves are actually written to during sync-up and the
-    # default sparse_size of 1Gig is too much for a regular /tmp, so
-    # let's use smaller ones here.
-    #
-    _sparse_size = 20*1024**2
-
-    @tag_test(TestTags.CORE)
-    def test_lvpartial(self):
-        """Verify that missing PVs are detected and can be dealt with"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev3, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2, self.loop_dev3], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev2)
-        self.assertTrue(info)
-        self.assertFalse(info.missing)
-        self.assertEqual(info.vg_name, "testVG")
-        loop_dev2_pv_uuid = info.pv_uuid
-
-        # Create a mirrored LV on the first two PVs
-        with wait_for_sync("testVG", "testLV"):
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 5 * 1024**2, "raid1",
-                                         [self.loop_dev, self.loop_dev2], None)
-            self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.attr[8], "-")
-
-        # Check that lvs_tree returns the expected structure
-
-        def assert_lv_subs(info, segtype, len_segs, len_data, len_metadata):
-            self.assertTrue(info)
-            self.assertEqual(info.segtype, segtype)
-            self.assertEqual(len(info.segs), len_segs)
-            self.assertEqual(len(info.data_lvs), len_data)
-            self.assertEqual(len(info.metadata_lvs), len_metadata)
-
-        def assert_lv_single_pv(info, pv):
-            if pv:
-                assert_lv_subs(info, "linear", 1, 0, 0)
-                self.assertEqual(info.segs[0].pvdev, pv)
-            else:
-                assert_lv_subs(info, "linear", 0, 0, 0)
-
-        def assert_raid1_structure(pv1, pv2):
-            lvs = { lv.lv_name: lv for lv in BlockDev.lvm_lvs_tree("testVG") }
-            info = lvs["testLV"]
-            assert_lv_subs(info, "raid1", 0, 2, 2)
-            assert_lv_single_pv(lvs["["+info.data_lvs[0]+"]"], pv1)
-            assert_lv_single_pv(lvs["["+info.data_lvs[1]+"]"], pv2)
-            assert_lv_single_pv(lvs["["+info.metadata_lvs[0]+"]"], pv1)
-            assert_lv_single_pv(lvs["["+info.metadata_lvs[1]+"]"], pv2)
-
-        assert_raid1_structure(self.loop_dev, self.loop_dev2)
-
-        # Disconnect the second PV, this should cause it to be flagged
-        # as missing, and testLV to be reported as "partial".
-        delete_lio_device(self.loop_dev2)
-
-        # Kick lvmdbusd so that it notices the missing PV.
-        dbus.SystemBus().call_blocking('com.redhat.lvmdbus1', '/com/redhat/lvmdbus1/Manager',
-                                       'com.redhat.lvmdbus1.Manager', 'Refresh', '', [])
-
-        pvs = BlockDev.lvm_pvs()
-        found = False
-        for pv in pvs:
-            if pv.pv_uuid == loop_dev2_pv_uuid:
-                found = True
-                self.assertTrue(pv.missing)
-                self.assertEqual(pv.vg_name, "testVG")
-        self.assertTrue(found)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.attr[8], "p")
-
-        # lvs_tree should report the second stripe to be missing
-        assert_raid1_structure(self.loop_dev, None)
-
-        # remove records of missing PVs
-        succ = BlockDev.lvm_vgreduce("testVG", None, None)
-        self.assertTrue(succ)
-
-        pvs = BlockDev.lvm_pvs()
-        found = False
-        for pv in pvs:
-            if pv.pv_uuid == loop_dev2_pv_uuid:
-                found = True
-        self.assertFalse(found)
-
-        # lvs_tree should still report the second stripe to be missing
-        assert_raid1_structure(self.loop_dev, None)
-
-        # repair testLV with the third PV
-        with wait_for_sync("testVG", "testLV"):
-            succ = BlockDev.lvm_lvrepair("testVG", "testLV", [self.loop_dev3])
-            self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.attr[8], "-")
-
-        assert_raid1_structure(self.loop_dev, self.loop_dev3)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVsAll(LvmPVVGthpoolTestCase):
-    def test_lvs_all(self):
-        """Verify that info is gathered for all LVs"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        # there should be at least 3 LVs -- testPool, [testPool_tdata], [testPool_tmeta] (plus probably some spare LVs)
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertGreater(len(lvs), 3)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestThpoolCreate(LvmPVVGthpoolTestCase):
-    @tag_test(TestTags.CORE)
-    def test_thpoolcreate(self):
-        """Verify that it is possible to create a thin pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-        self.assertIn("private", info.roles.split(","))
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestThpoolConvert(LvmPVVGthpoolTestCase):
-    def test_thpool_convert(self):
-        """Verify that it is possible to create a thin pool by conversion"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # the name of the data LV is used for the pool
-        succ = BlockDev.lvm_lvcreate("testVG", "dataLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_lvcreate("testVG", "metadataLV", 50 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpool_convert("testVG", "dataLV", "metadataLV", "testPool", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestDataMetadataLV(LvmPVVGthpoolTestCase):
-    def test_data_metadata_lv_name(self):
-        """Verify that it is possible to get name of the data/metadata LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        lvi = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertTrue(lvi.data_lv)
-        self.assertTrue(lvi.data_lv.startswith("testPool"))
-        self.assertIn("_tdata", lvi.data_lv)
-
-        info = BlockDev.lvm_lvinfo("testVG", lvi.data_lv)
-        self.assertTrue(info.attr.startswith("T"))
-        self.assertIn("private", info.roles.split(","))
-        self.assertIn("data", info.roles.split(","))
-
-        lvi = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertTrue(lvi.metadata_lv)
-        self.assertTrue(lvi.metadata_lv.startswith("testPool"))
-        self.assertIn("_tmeta", lvi.metadata_lv)
-
-        info = BlockDev.lvm_lvinfo("testVG", lvi.metadata_lv)
-        self.assertTrue(info.attr.startswith("e"))
-        self.assertIn("private", info.roles.split(","))
-        self.assertIn("metadata", info.roles.split(","))
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGLVthLVTestCase(LvmPVVGthpoolTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testThLV", True, None)
-        except:
-            pass
-
-        LvmPVVGthpoolTestCase._clean_up(self)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestThLVcreate(LvmPVVGLVthLVTestCase):
-    @tag_test(TestTags.CORE)
-    def test_thlvcreate_thpoolname(self):
-        """Verify that it is possible to create a thin LV and get its pool name"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, None, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thlvcreate("testVG", "testPool", "testThLV", 1024**3, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testThLV")
-        self.assertIn("V", info.attr)
-
-        pool = BlockDev.lvm_thlvpoolname("testVG", "testThLV")
-        lvi = BlockDev.lvm_lvinfo("testVG", "testThLV")
-        self.assertEqual(pool, "testPool")
-        self.assertEqual(lvi.pool_lv, "testPool")
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGLVthLVsnapshotTestCase(LvmPVVGLVthLVTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testThLV_bak", True, None)
-        except:
-            pass
-
-        LvmPVVGLVthLVTestCase._clean_up(self)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestThSnapshotCreate(LvmPVVGLVthLVsnapshotTestCase):
-    def test_thsnapshotcreate(self):
-        """Verify that it is possible to create a thin LV snapshot"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, None, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thlvcreate("testVG", "testPool", "testThLV", 1024**3, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testThLV")
-        self.assertIn("V", info.attr)
-
-        succ = BlockDev.lvm_thsnapshotcreate("testVG", "testThLV", "testThLV_bak", "testPool", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testThLV_bak")
-        self.assertIn("V", info.attr)
-        self.assertIn("snapshot", info.roles.split(","))
-        self.assertIn("thinsnapshot", info.roles.split(","))
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGLVcachePoolTestCase(LvmPVVGLVTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testCache", True, None)
-        except:
-            pass
-
-        # lets help udev with removing stale symlinks
-        try:
-            if not BlockDev.lvm_lvs("testVG") and os.path.exists("/dev/testVG/testCache_meta"):
-                shutil.rmtree("/dev/testVG", ignore_errors=True)
-        except:
-            pass
-
-        LvmPVVGLVTestCase._clean_up(self)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGLVcachePoolCreateRemoveTestCase(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW, TestTags.UNSTABLE)
-    def test_cache_pool_create_remove(self):
-        """Verify that is it possible to create and remove a cache pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvremove("testVG", "testCache", True, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITEBACK,
-                                              BlockDev.LVMCachePoolFlags.STRIPED|BlockDev.LVMCachePoolFlags.META_RAID1,
-                                              [self.loop_dev, self.loop_dev2])
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestCachePoolConvert(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_pool_convert(self):
-        """Verify that it is possible to create a cache pool by conversion"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "dataLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_lvcreate("testVG", "metadataLV", 50 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_pool_convert("testVG", "dataLV", "metadataLV", "testCache", None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGLVcachePoolAttachDetachTestCase(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_pool_attach_detach(self):
-        """Verify that is it possible to attach and detach a cache pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        # detach and destroy (the last arg)
-        succ = BlockDev.lvm_cache_detach("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # once more and do not destroy this time
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_detach("testVG", "testLV", False, None)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertTrue(any(info.lv_name == "testCache" for info in lvs))
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGcachedLVTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_create_cached_lv(self):
-        """Verify that it is possible to create a cached LV in a single step"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_cached_lv("testVG", "testLV", 512 * 1024**2, 256 * 1024**2, 10 * 1024**2,
-                                                   BlockDev.LVMCacheMode.WRITEBACK, 0,
-                                                   [self.loop_dev], [self.loop_dev2])
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGcachedLVpoolTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_get_pool_name(self):
-        """Verify that it is possible to get the name of the cache pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        lvm_version = self._get_lvm_version()
-        if lvm_version < Version("2.03.06"):
-            cpool_name = "testCache"
-        else:
-            # since 2.03.06 LVM adds _cpool suffix to the cache pool after attaching it
-            cpool_name = "testCache_cpool"
-
-        self.assertEqual(BlockDev.lvm_cache_pool_name("testVG", "testLV"), cpool_name)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGLVWritecacheAttachDetachTestCase(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_writecache_attach_detach(self):
-        """Verify that is it possible to attach and detach a writecache LV"""
-
-        lvm_version = self._get_lvm_version()
-        if lvm_version < Version("2.03.10"):
-            self.skipTest("LVM writecache support in DBus API not available")
-
-        lvm_segtypes = self._get_lvm_segtypes()
-        if "writecache" not in lvm_segtypes:
-            self.skipTest("LVM writecache support not available")
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testCache", 512 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertIsNotNone(info)
-        self.assertEqual(info.segtype, "writecache")
-
-        # detach and destroy (the last arg)
-        succ = BlockDev.lvm_writecache_detach("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # once more and do not destroy this time
-        succ = BlockDev.lvm_lvcreate("testVG", "testCache", 512 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_detach("testVG", "testLV", False, None)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertTrue(any(info.lv_name == "testCache" for info in lvs))
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGWritecachedLVTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_create_cached_lv(self):
-        """Verify that it is possible to create a cached LV in a single step"""
-
-        lvm_version = self._get_lvm_version()
-        if lvm_version < Version("2.03.10"):
-            self.skipTest("LVM writecache support in DBus API not available")
-
-        lvm_segtypes = self._get_lvm_segtypes()
-        if "writecache" not in lvm_segtypes:
-            self.skipTest("LVM writecache support not available")
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_create_cached_lv("testVG", "testLV", 512 * 1024**2, 256 * 1024**2,
-                                                        [self.loop_dev], [self.loop_dev2])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertIsNotNone(info)
-        self.assertEqual(info.segtype, "writecache")
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmPVVGcachedLVstatsTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_get_stats(self):
-        """Verify that it is possible to get stats for a cached LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        stats = BlockDev.lvm_cache_stats("testVG", "testLV")
-        self.assertTrue(stats)
-        self.assertEqual(stats.cache_size, 512 * 1024**2)
-        self.assertEqual(stats.md_size, 8 * 1024**2)
-        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
-
-class LvmPVVGcachedThpoolstatsTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_get_stats(self):
-        """Verify that it is possible to get stats for a cached thinpool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testPool", "testCache", None)
-        self.assertTrue(succ)
-
-        # just ask for the pool itself even if it's not technically cached
-        stats = BlockDev.lvm_cache_stats("testVG", "testPool")
-        self.assertTrue(stats)
-        self.assertEqual(stats.cache_size, 512 * 1024**2)
-        self.assertEqual(stats.md_size, 8 * 1024**2)
-        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
-
-        # same should work when explicitly asking for the data LV
-        stats = BlockDev.lvm_cache_stats("testVG", "testPool_tdata")
-        self.assertTrue(stats)
-        self.assertEqual(stats.cache_size, 512 * 1024**2)
-        self.assertEqual(stats.md_size, 8 * 1024**2)
-        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
+        _lvm_cases.LvmNoDevTestCase.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LVMTechTest(LVMTestCase):
+    @tag_test(TestTags.NOSTORAGE)
+    def test_plugin_version(self):
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.LVM), "libbd_lvm-dbus.so.3")
 
     @tag_test(TestTags.NOSTORAGE)
     def test_tech_available(self):
@@ -1978,7 +54,7 @@ class LVMTechTest(LVMTestCase):
         _ret, _out, _err = run_command("systemctl stop lvm2-lvmdbusd")
 
         # reinit libblockdev -- init checks are switched off so nothing should start the service
-        self.assertTrue(BlockDev.reinit([self.ps, self.ps2], True, None))
+        self.assertTrue(BlockDev.reinit(self.requested_plugins, True, None))
         ret, _out, _err = run_command("systemctl status lvm2-lvmdbusd")
         self.assertNotEqual(ret, 0)
 
@@ -1993,411 +69,77 @@ class LVMTechTest(LVMTestCase):
         with self.assertRaisesRegex(GLib.GError, "Only 'query' supported for thin calculations"):
             BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.THIN_CALCS, BlockDev.LVMTechMode.CREATE)
 
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestPVremoveConfig(LvmPVonlyTestCase):
-    def test_pvremove_with_config(self):
-        """Verify that we correctly pass extra arguments when calling PvRemove"""
-
-        # we add some extra arguments to PvRemove (like '-ff') and we want
-        # to be sure that adding these works together with '--config'
-
-        BlockDev.lvm_set_global_config("backup {backup=0 archive=0}")
-        self.addCleanup(BlockDev.lvm_set_global_config, None)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # we are removing pv that is part of vg -- '-ff' option must be included
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvremove(self.loop_dev2, None)
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-@unittest.skip("LVM DBus crashes if an exported VG is present in the system")
-class LvmVGExportedTestCase(LvmPVVGLVTestCase):
-
-    def _clean_up(self):
-        run_command("vgimport testVG")
-
-        LvmPVVGLVTestCase._clean_up(self)
-
-    @tag_test(TestTags.SLOW)
-    def test_exported_vg(self):
-        """Verify that info has correct information about exported VGs"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertFalse(info.exported)
-
-        ret, out, err = run_command("vgexport testVG")
-        if ret != 0:
-            self.fail("Failed to export VG:\n%s %s" % (out, err))
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertTrue(info.exported)
-
-
-        self.assertTrue(succ)
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LvmTestLVTags(LvmPVVGLVTestCase):
-    def test_vgtags(self):
-        """Verify that it's possible to set and get info about LV tags"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertFalse(info.lv_tags)
-
-        succ = BlockDev.lvm_add_lv_tags("testVG", "testLV", ["a", "b", "c"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_tags, ["a", "b", "c"])
-
-        succ = BlockDev.lvm_delete_lv_tags("testVG", "testLV", ["a", "b"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_tags, ["c"])
-
-        succ = BlockDev.lvm_add_lv_tags("testVG", "testLV", ["e"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_tags, ["c", "e"])
-
-@unittest.skipUnless(lvm_dbus_running, "LVM DBus not running")
-class LVMVDOTest(LVMTestCase):
-
-    loop_size = 8 * 1024**3
 
+class LvmVDOTest(_lvm_cases.LvmVDOTest, LvmDBusTestCase):
     @classmethod
     def setUpClass(cls):
-        if not BlockDev.utils_have_kernel_module("dm-vdo"):
-            raise unittest.SkipTest("VDO kernel module not available, skipping.")
-
-        try:
-            BlockDev.utils_load_kernel_module("dm-vdo")
-        except GLib.GError as e:
-            if "File exists" not in e.message:
-                raise unittest.SkipTest("cannot load VDO kernel module, skipping.")
-
-        lvm_version = cls._get_lvm_version()
-        if lvm_version < Version("2.3.07"):
-            raise unittest.SkipTest("LVM version 2.3.07 or newer needed for LVM VDO.")
-
-        super().setUpClass()
-
-    def setUp(self):
-        self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("vdo_test", self.loop_size)
-        try:
-            self.loop_dev = create_lio_device(self.dev_file)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVDOVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVDOVG", "vdoPool", True, None)
-        except:
-            pass
-
-        BlockDev.lvm_vgremove("testVDOVG")
-        BlockDev.lvm_pvremove(self.loop_dev)
-
-        # XXX remove lingering /dev entries
-        shutil.rmtree("/dev/testVDOVG", ignore_errors=True)
-
-        try:
-            delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
-
-    @tag_test(TestTags.SLOW, TestTags.CORE)
-    def test_vdo_pool_create(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
-        self.assertIsNotNone(lv_info)
-        self.assertEqual(lv_info.segtype, "vdo")
-        self.assertEqual(lv_info.pool_lv, "vdoPool")
-
-        pool_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool")
-        self.assertEqual(pool_info.segtype, "vdo-pool")
-        self.assertEqual(pool_info.data_lv, "vdoPool_vdata")
-        lvm_version = self._get_lvm_version()
-        if lvm_version >= Version("2.03.24"):
-            self.assertGreater(pool_info.data_percent, 0)
-
-        pool = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV")
-        self.assertEqual(pool, lv_info.pool_lv)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertEqual(vdo_info.operating_mode, BlockDev.LVMVDOOperatingMode.NORMAL)
-        self.assertEqual(vdo_info.compression_state, BlockDev.LVMVDOCompressionState.ONLINE)
-        self.assertTrue(vdo_info.compression)
-        self.assertTrue(vdo_info.deduplication)
-        self.assertGreater(vdo_info.index_memory_size, 0)
-        self.assertGreater(vdo_info.used_size, 0)
-        self.assertTrue(0 <= vdo_info.saving_percent <= 100)
-
-        lvs = BlockDev.lvm_lvs("testVDOVG")
-        self.assertIn("vdoPool", [l.lv_name for l in lvs])
-        self.assertIn("vdoLV", [l.lv_name for l in lvs])
-
-        mode_str = BlockDev.lvm_get_vdo_operating_mode_str(vdo_info.operating_mode)
-        self.assertEqual(mode_str, "normal")
-
-        state_str = BlockDev.lvm_get_vdo_compression_state_str(vdo_info.compression_state)
-        self.assertEqual(state_str, "online")
-
-        policy_str = BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy)
-        self.assertIn(policy_str, ["sync", "async", "auto"])
-
-        state_str = BlockDev.lvm_get_vdo_compression_state_str(vdo_info.compression_state)
-        self.assertEqual(state_str, "online")
-
-    @tag_test(TestTags.SLOW)
-    def test_vdo_pool_create_options(self):
-        # set index size to 300 MiB, disable compression and write policy to sync
-        policy = BlockDev.lvm_get_vdo_write_policy_from_str("sync")
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3,
-                                            300 * 1024**2, False, True, policy)
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertEqual(vdo_info.index_memory_size, 300 * 1024**2)
-        self.assertFalse(vdo_info.compression)
-        self.assertTrue(vdo_info.deduplication)
-        self.assertEqual(BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy), "sync")
-
-    @tag_test(TestTags.SLOW)
-    def test_vdo_pool_create_noname(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", None, 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
-        self.assertIsNotNone(lv_info)
-        self.assertEqual(lv_info.segtype, "vdo")
-
-        pool_name = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV")
-        self.assertEqual(lv_info.pool_lv, pool_name)
-        pool_info = BlockDev.lvm_lvinfo("testVDOVG", pool_name)
-        self.assertEqual(pool_info.segtype, "vdo-pool")
-
-    @tag_test(TestTags.SLOW)
-    def test_resize(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 5 * 1024**3, 10 * 1024**3)
-        self.assertTrue(succ)
-
-        # "physical" resize first (pool), shrinking not allowed
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vdo_pool_resize("testVDOVG", "vdoPool", 4 * 1024**3)
-
-        succ = BlockDev.lvm_vdo_pool_resize("testVDOVG", "vdoPool", 7 * 1024**3)
-        self.assertTrue(succ)
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool")
-        self.assertEqual(lv_info.size, 7 * 1024**3)
-
-        # "logical" resize (LV)
-        succ = BlockDev.lvm_vdo_resize("testVDOVG", "vdoLV", 35 * 1024**3)
-        self.assertTrue(succ)
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
-        self.assertEqual(lv_info.size, 35 * 1024**3)
-
-    @tag_test(TestTags.SLOW)
-    def test_enable_disable_compression(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3,
-                                            300 * 1024**2)
-        self.assertTrue(succ)
-
-        # enabled by default
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.compression)
-
-        # disable compression
-        succ = BlockDev.lvm_vdo_disable_compression("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertFalse(vdo_info.compression)
-
-        # and enable compression back
-        succ = BlockDev.lvm_vdo_enable_compression("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.compression)
-
-    @tag_test(TestTags.SLOW)
-    def test_enable_disable_deduplication(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3,
-                                            300 * 1024**2)
-        self.assertTrue(succ)
-
-        # enabled by default
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.deduplication)
-
-        # disable compression
-        succ = BlockDev.lvm_vdo_disable_deduplication("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertFalse(vdo_info.deduplication)
-
-        # and enable compression back
-        succ = BlockDev.lvm_vdo_enable_deduplication("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.deduplication)
+        _lvm_cases.LvmVDOTest.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
     @tag_test(TestTags.SLOW)
     def test_vdo_pool_convert(self):
         self.skipTest("LVM VDO pool convert not implemented in LVM DBus API.")
 
-    @tag_test(TestTags.SLOW)
-    def test_stats(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.deduplication)
-
-        vdo_stats = BlockDev.lvm_vdo_get_stats("testVDOVG", "vdoPool")
-
-        # just sanity check
-        self.assertNotEqual(vdo_stats.saving_percent, -1)
-        self.assertNotEqual(vdo_stats.used_percent, -1)
-        self.assertNotEqual(vdo_stats.block_size, -1)
-        self.assertNotEqual(vdo_stats.logical_block_size, -1)
-        self.assertNotEqual(vdo_stats.physical_blocks, -1)
-        self.assertNotEqual(vdo_stats.data_blocks_used, -1)
-        self.assertNotEqual(vdo_stats.overhead_blocks_used, -1)
-        self.assertNotEqual(vdo_stats.logical_blocks_used, -1)
-        self.assertNotEqual(vdo_stats.write_amplification_ratio, -1)
-
-        full_stats = BlockDev.lvm_vdo_get_stats_full("testVDOVG", "vdoPool")
-        self.assertIn("writeAmplificationRatio", full_stats.keys())
-
-
-class LvmTestDevicesFile(LvmPVonlyTestCase):
-    devicefile = "bd_lvm_dbus_tests.devices"
 
+class LvmTestPVs(_lvm_cases.LvmTestPVs, LvmDBusTestCase):
     @classmethod
-    def tearDownClass(cls):
-        try:
-            os.remove("/etc/lvm/devices/" + cls.devicefile)
-        except FileNotFoundError:
-            pass
-
-        super(LvmTestDevicesFile, cls).tearDownClass()
+    def setUpClass(cls):
+        _lvm_cases.LvmTestPVs.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-    def test_devices_add_delete(self):
-        if not self.devices_avail:
-            self.skipTest("skipping LVM devices file test: not supported")
 
-        self.addCleanup(BlockDev.lvm_set_global_config, None)
+class LvmTestVGs(_lvm_cases.LvmTestVGs, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestVGs.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-        # force-enable the feature, it might be disabled by default
-        succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=1 }")
-        self.assertTrue(succ)
 
-        succ = BlockDev.lvm_pvcreate(self.loop_dev)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_devices_add("/non/existing/device", self.devicefile)
+class LvmTestLVs(_lvm_cases.LvmTestLVs, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestLVs.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
 
-        succ = BlockDev.lvm_devices_add(self.loop_dev, self.devicefile)
-        self.assertTrue(succ)
+class LvmDBusTestLVcreateType(_lvm_cases.LvmPVVGLVTestCase, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmPVVGLVTestCase.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-        dfile = read_file("/etc/lvm/devices/" + self.devicefile)
-        self.assertIn(self.loop_dev, dfile)
 
-        succ = BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
-        self.assertTrue(succ)
-
-        dfile = read_file("/etc/lvm/devices/" + self.devicefile)
-        self.assertNotIn(self.loop_dev, dfile)
+class LvmTestPartialLVs(_lvm_cases.LvmTestPartialLVs, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestPartialLVs.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-        BlockDev.lvm_set_global_config(None)
 
-    def test_devices_enabled(self):
-        if not self.devices_avail:
-            self.skipTest("skipping LVM devices file test: not supported")
+class LvmTestThpool(_lvm_cases.LvmTestThpool, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestThpool.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-        self.addCleanup(BlockDev.lvm_set_global_config, None)
 
-        # checking if the feature is enabled or disabled is hard so lets just disable
-        # the devices file using the global config and check lvm_devices_add fails
-        # with the correct exception message
-        succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=0 }")
-        self.assertTrue(succ)
+class LvmTestThLV(_lvm_cases.LvmTestThLV, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestThLV.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-        with self.assertRaisesRegex(GLib.GError, "LVM devices file not enabled."):
-            BlockDev.lvm_devices_add("", self.devicefile)
 
+class LvmTestCache(_lvm_cases.LvmTestCache, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestCache.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-class LvmConfigTestPvremove(LvmPVonlyTestCase):
 
-    @tag_test(TestTags.REGRESSION)
-    def test_set_empty_config(self):
-        succ = BlockDev.lvm_pvcreate(self.loop_dev)
-        self.assertTrue(succ)
+class LvmTestDevicesFile(_lvm_cases.LvmTestDevicesFile, LvmDBusTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestDevicesFile.setUpClass()
+        LvmDBusTestCase.setUpClass()
 
-        BlockDev.lvm_set_global_config("")
-        succ = BlockDev.lvm_pvremove(self.loop_dev)
-        self.assertTrue(succ)
diff -pruN 3.3.1-3/tests/lvm_test.py 3.4.0-1/tests/lvm_test.py
--- 3.3.1-3/tests/lvm_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/lvm_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -1,15 +1,6 @@
-from __future__ import division
-import unittest
-import os
-import math
-import overrides_hack
-import re
-import shutil
-import time
-from contextlib import contextmanager
-from packaging.version import Version
+import _lvm_cases
 
-from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, TestTags, tag_test, run_command, read_file, required_plugins
+from utils import TestTags, tag_test, required_plugins, fake_path
 
 import gi
 gi.require_version('GLib', '2.0')
@@ -17,23 +8,10 @@ gi.require_version('BlockDev', '3.0')
 from gi.repository import GLib, BlockDev
 
 
-@contextmanager
-def wait_for_sync(vg_name, lv_name):
-    try:
-        yield
-    finally:
-        time.sleep(2)
-        while True:
-            info = BlockDev.lvm_lvinfo(vg_name, lv_name)
-            if not info:
-                break
-            if info.copy_percent == 100:
-                break
-            time.sleep(1)
-
 
 @required_plugins(("lvm",))
-class LVMTestCase(unittest.TestCase):
+class LvmTestCase(_lvm_cases.LvmTestCase):
+    test_type = "cli"
 
     @classmethod
     def setUpClass(cls):
@@ -50,1905 +28,17 @@ class LVMTestCase(unittest.TestCase):
         except:
             cls.devices_avail = False
 
-    @classmethod
-    def _get_lvm_version(cls):
-        _ret, out, _err = run_command("lvm version")
-        m = re.search(r"LVM version:\s+([\d\.]+)", out)
-        if not m or len(m.groups()) != 1:
-            raise RuntimeError("Failed to determine LVM version from: %s" % out)
-        return Version(m.groups()[0])
-
-    @classmethod
-    def _get_lvm_segtypes(cls):
-        _ret, out, _err = run_command("lvm segtypes")
-        return out
-
-
-class LvmNoDevTestCase(LVMTestCase):
-    def __init__(self, *args, **kwargs):
-        super(LvmNoDevTestCase, self).__init__(*args, **kwargs)
-
-    def setUp(self):
-        self._log = ""
 
+class LvmNoDevTestCase(_lvm_cases.LvmNoDevTestCase, LvmTestCase):
     @classmethod
     def setUpClass(cls):
-        # we are checking for info log messages and default level is warning
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_INFO)
-
-        super(LvmNoDevTestCase, cls).setUpClass()
-
-    @classmethod
-    def tearDownClass(cls):
-        # reset back to default
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_WARNING)
-        BlockDev.lvm_set_global_config(None)
-
-        super(LvmNoDevTestCase, cls).tearDownClass()
+        _lvm_cases.LvmNoDevTestCase.setUpClass()
+        LvmTestCase.setUpClass()
 
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.LVM), "libbd_lvm.so.3")
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_is_supported_pe_size(self):
-        """Verify that lvm_is_supported_pe_size works as expected"""
-
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(6 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(12 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(15 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_supported_pe_size(4 * 1024**3))
-
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(512))
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(4097))
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(65535))
-        self.assertFalse(BlockDev.lvm_is_supported_pe_size(32 * 1024**3))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_supported_pe_sizes(self):
-        """Verify that supported PE sizes are really supported"""
-
-        supported = BlockDev.lvm_get_supported_pe_sizes()
-
-        for size in supported:
-            self.assertTrue(BlockDev.lvm_is_supported_pe_size(size))
-
-        self.assertIn(4 * 1024, supported)
-        self.assertIn(4 * 1024 **2, supported)
-        self.assertIn(16 * 1024**3, supported)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_max_lv_size(self):
-        """Verify that max LV size is correctly determined"""
-
-        if os.uname()[-1] == "i686":
-            # 32-bit arch
-            expected = 16 * 1024**4
-        else:
-            # 64-bit arch
-            expected = 8 * 1024**6
-
-        self.assertEqual(BlockDev.lvm_get_max_lv_size(), expected)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_round_size_to_pe(self):
-        """Verify that round_size_to_pe works as expected"""
-
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 4 * 1024**2, True), 12 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 4 * 1024**2, False), 8 * 1024**2)
-
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 6 * 1024**2, True), 12 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 6 * 1024**2, False), 6 * 1024**2)
-
-        # default PE size is 4 MiB
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 0, True), 12 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(11 * 1024**2, 0, False), 8 * 1024**2)
-
-        # cannot round up to GLib.MAXUINT64, but can round up over GLib.MAXUINT64 (should round down in that case)
-        biggest_multiple = (GLib.MAXUINT64 // (4 * 1024**2)) * (4 * 1024**2)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple + (2 * 1024**2), 4 * 1024**2, True),
-                         biggest_multiple)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple + (2 * 1024**2), 4 * 1024**2, False),
-                         biggest_multiple)
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple - (2 * 4 * 1024**2) + 1, 4 * 1024**2, True),
-                         biggest_multiple - (4 * 1024**2))
-        self.assertEqual(BlockDev.lvm_round_size_to_pe(biggest_multiple - (2 * 4 * 1024**2) + 1, 4 * 1024**2, False),
-                         biggest_multiple - (2 * 4 * 1024**2))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_lv_physical_size(self):
-        """Verify that get_lv_physical_size works as expected"""
-
-        self.assertEqual(BlockDev.lvm_get_lv_physical_size(25 * 1024**3, 4 * 1024**2),
-                         25 * 1024**3)
-
-        # default PE size is 4 MiB
-        self.assertEqual(BlockDev.lvm_get_lv_physical_size(25 * 1024**3, 0),
-                         25 * 1024**3)
-
-        self.assertEqual(BlockDev.lvm_get_lv_physical_size(11 * 1024**2, 4 * 1024**2),
-                         12 * 1024**2)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_thpool_padding(self):
-        """Verify that get_thpool_padding works as expected"""
-
-        expected_padding = BlockDev.lvm_round_size_to_pe(int(math.ceil(11 * 1024**2 * 0.2)),
-                                                         4 * 1024**2, True)
-        self.assertEqual(BlockDev.lvm_get_thpool_padding(11 * 1024**2, 4 * 1024**2, False),
-                         expected_padding)
-
-        expected_padding = BlockDev.lvm_round_size_to_pe(int(math.ceil(11 * 1024**2 * (1.0/6.0))),
-                                                         4 * 1024**2, True)
-        self.assertEqual(BlockDev.lvm_get_thpool_padding(11 * 1024**2, 4 * 1024**2, True),
-                         expected_padding)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_thpool_meta_size(self):
-        """Verify that getting recommended thin pool metadata size works as expected"""
-
-
-        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 64 * 1024),
-                         1 * 1024**3)
-
-        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(1 * 1024**4, 128 * 1024),
-                         512 * 1024**2)
-
-        # min metadata size is 4 MiB
-        self.assertEqual(BlockDev.lvm_get_thpool_meta_size(100 * 1024**2, 128 * 1024),
-                         4 * 1024**2)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_is_valid_thpool_md_size(self):
-        """Verify that is_valid_thpool_md_size works as expected"""
-
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(4 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(5 * 1024**2))
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_md_size(15 * 1024**3))
-
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(1 * 1024**2))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(3 * 1024**2))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(16 * 1024**3))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_md_size(32 * 1024**3))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_is_valid_thpool_chunk_size(self):
-        """Verify that is_valid_thpool_chunk_size works as expected"""
-
-        # 64 KiB is OK with or without discard
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(64 * 1024, True))
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(64 * 1024, False))
-
-        # 192 KiB is OK without discard, but NOK with discard
-        self.assertTrue(BlockDev.lvm_is_valid_thpool_chunk_size(192 * 1024, False))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(192 * 1024, True))
-
-        # 191 KiB is NOK in both cases
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(191 * 1024, False))
-        self.assertFalse(BlockDev.lvm_is_valid_thpool_chunk_size(191 * 1024, True))
-
-    def _store_log(self, lvl, msg):
-        self._log += str((lvl, msg))
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_set_global_config(self):
-        """Verify that getting and setting global config works as expected"""
-
-        # setup logging
-        self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self._store_log))
-
-        # no global config set initially
-        self.assertEqual(BlockDev.lvm_get_global_config(), "")
-
-        # set and try to get back
-        succ = BlockDev.lvm_set_global_config("bla")
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
-
-        # reset and try to get back
-        succ = BlockDev.lvm_set_global_config(None)
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_global_config(), "")
-
-        # set twice and try to get back twice
-        succ = BlockDev.lvm_set_global_config("foo")
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_set_global_config("bla")
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
-        self.assertEqual(BlockDev.lvm_get_global_config(), "bla")
-
-        # set something sane and check it's really used
-        succ = BlockDev.lvm_set_global_config("backup {backup=0 archive=0}")
-        self.assertTrue(succ)
-        BlockDev.lvm_lvs(None)
-        self.assertIn("--config=backup {backup=0 archive=0}", self._log)
-
-        # reset back to default
-        succ = BlockDev.lvm_set_global_config(None)
-        self.assertTrue(succ)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_get_set_global_devices_filter(self):
-        """Verify that getting and setting LVM devices filter works as expected"""
-        if not self.devices_avail:
-            self.skipTest("skipping LVM devices filter test: not supported")
-
-        # setup logging
-        self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self._store_log))
-
-        # no global config set initially
-        self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
-
-        # set and try to get back
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
-        self.assertTrue(succ)
-        self.assertListEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sda"])
-
-        # reset and try to get back
-        succ = BlockDev.lvm_set_devices_filter(None)
-        self.assertTrue(succ)
-        self.assertListEqual(BlockDev.lvm_get_devices_filter(), [])
-
-        # set twice and try to get back twice
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sda"])
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sdb"])
-        self.assertTrue(succ)
-        self.assertEqual(BlockDev.lvm_get_devices_filter(), ["/dev/sdb"])
-
-        # set something sane and check it's really used
-        succ = BlockDev.lvm_set_devices_filter(["/dev/sdb", "/dev/sdc"])
-        self.assertTrue(succ)
-        BlockDev.lvm_lvs(None)
-        self.assertIn("--devices=/dev/sdb,/dev/sdc", self._log)
-
-        # reset back to default
-        succ = BlockDev.lvm_set_devices_filter(None)
-        self.assertTrue(succ)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_cache_get_default_md_size(self):
-        """Verify that default cache metadata size is calculated properly"""
-
-        # 1000x smaller than the data LV size, but at least 8 MiB
-        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(100 * 1024**3), (100 * 1024**3) // 1000)
-        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(80 * 1024**3), (80 * 1024**3) // 1000)
-        self.assertEqual(BlockDev.lvm_cache_get_default_md_size(6 * 1024**3), 8 * 1024**2)
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_cache_mode_bijection(self):
-        """Verify that cache modes and their string representations map to each other"""
-
-        mode_strs = {BlockDev.LVMCacheMode.WRITETHROUGH: "writethrough",
-                     BlockDev.LVMCacheMode.WRITEBACK: "writeback",
-                     BlockDev.LVMCacheMode.UNKNOWN: "unknown",
-        }
-        for mode in mode_strs.keys():
-            self.assertEqual(BlockDev.lvm_cache_get_mode_str(mode), mode_strs[mode])
-            self.assertEqual(BlockDev.lvm_cache_get_mode_from_str(mode_strs[mode]), mode)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_cache_get_mode_from_str("bla")
-
-    @tag_test(TestTags.NOSTORAGE)
-    def test_lvm_config(self):
-        """Verify that we can correctly read from LVM config"""
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_config_get(None, "dir")
-
-        # get entire config
-        conf = BlockDev.lvm_config_get()
-        self.assertTrue(conf)
-        self.assertTrue(conf.startswith("config"))
-
-        # get just the "devices" section
-        conf = BlockDev.lvm_config_get("devices")
-        self.assertTrue(conf)
-        self.assertTrue(conf.startswith("devices"))
-
-        # let's be brave and assume devices/dir is set everywhere ti /dev
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full")
-        self.assertEqual(devdir, "\"/dev\"")
-
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full", values_only=False)
-        self.assertEqual(devdir, "dir=\"/dev\"")
-
-        devdir = BlockDev.lvm_config_get("devices", "dir", "default")
-        self.assertEqual(devdir, "\"/dev\"")
-
-        # let's try to override some results with --config
-        BlockDev.lvm_set_global_config("devices/dir=/test")
-
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full")
-        self.assertEqual(devdir, "\"/test\"")
-
-        # "default" config should not be affected by --config
-        devdir = BlockDev.lvm_config_get("devices", "dir", "default")
-        self.assertEqual(devdir, "\"/dev\"")
-
-        # disable global config
-        devdir = BlockDev.lvm_config_get("devices", "dir", "full", global_config=False)
-        self.assertEqual(devdir, "\"/dev\"")
-
-
-class LvmPVonlyTestCase(LVMTestCase):
-
-    _sparse_size = 1024**3
-
-    # :TODO:
-    #     * test pvmove (must create two PVs, a VG, a VG and some data in it
-    #       first)
-    #     * some complex test for pvs, vgs, lvs, pvinfo, vginfo and lvinfo
-    def setUp(self):
-        self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("lvm_test", self._sparse_size)
-        self.dev_file2 = create_sparse_tempfile("lvm_test", self._sparse_size)
-        self.dev_file3 = create_sparse_tempfile("lvm_test", self._sparse_size)
-        try:
-            self.loop_dev = create_lio_device(self.dev_file)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev2 = create_lio_device(self.dev_file2)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev3 = create_lio_device(self.dev_file3)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-
-    def _clean_up(self):
-        for dev in (self.loop_dev, self.loop_dev2, self.loop_dev3):
-            try:
-                BlockDev.lvm_pvremove(dev)
-            except:
-                pass
-
-            try:
-                BlockDev.lvm_devices_delete(dev)
-            except:
-                pass
-
-        try:
-            delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
-
-        try:
-            delete_lio_device(self.loop_dev2)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file2)
-
-        try:
-            delete_lio_device(self.loop_dev3)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file3)
-
-class LvmTestPVcreateRemove(LvmPVonlyTestCase):
-    @tag_test(TestTags.CORE)
-    def test_pvcreate_and_pvremove(self):
-        """Verify that it's possible to create and destroy a PV"""
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_pvcreate("/non/existing/device", 0, 0, None)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-        # this time try to specify data_alignment and metadata_size
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 2*1024**2, 4*1024**2, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_pvremove("/non/existing/device", None)
-
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-        # already removed -- not an issue
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-class LvmTestPVresize(LvmPVonlyTestCase):
-    def test_pvresize(self):
-        """Verify that it's possible to resize a PV"""
-
-        with self.assertRaises(GLib.GError):
-            succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None)
-
-        with self.assertRaises(GLib.GError):
-            succ = BlockDev.lvm_pvresize("/non/existing/device", 200 * 1024**2, None)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**2, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertEqual(info.pv_size, 200 * 1024**2)
-
-        succ = BlockDev.lvm_pvresize(self.loop_dev, 200 * 1024**3, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertEqual(info.pv_size, 200 * 1024**3)
-
-class LvmTestPVscan(LvmPVonlyTestCase):
-    def test_pvscan(self):
-        """Verify that pvscan runs without issues with cache or without"""
-
-        succ = BlockDev.lvm_pvscan(None, False, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvscan(self.loop_dev, True, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvscan(None, True, None)
-        self.assertTrue(succ)
-
-class LvmTestPVinfo(LvmPVonlyTestCase):
-    def test_pvinfo(self):
-        """Verify that it's possible to gather info about a PV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_name, self.loop_dev)
-        self.assertTrue(info.pv_uuid)
-
-class LvmTestPVs(LvmPVonlyTestCase):
-    def test_pvs(self):
-        """Verify that it's possible to gather info about PVs"""
-
-        pvs = BlockDev.lvm_pvs()
-        orig_len = len(pvs)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        pvs = BlockDev.lvm_pvs()
-        self.assertGreater(len(pvs), orig_len)
-        self.assertTrue(any(info.pv_name == self.loop_dev for info in pvs))
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-
-        self.assertTrue(any(info.pv_uuid == all_info.pv_uuid for all_info in pvs))
-
-class LvmPVVGTestCase(LvmPVonlyTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_vgremove("testVG", None)
-        except:
-            pass
-
-        # XXX remove lingering /dev entries
-        shutil.rmtree("/dev/testVG", ignore_errors=True)
-
-        LvmPVonlyTestCase._clean_up(self)
-
-class LvmTestVGcreateRemove(LvmPVVGTestCase):
-    @tag_test(TestTags.CORE)
-    def test_vgcreate_vgremove(self):
-        """Verify that it is possible to create and destroy a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgcreate("testVG", ["/non/existing/device"], 0, None)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # VG already exists
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-
-        succ = BlockDev.lvm_vgremove("testVG", None)
-        self.assertTrue(succ)
-
-        # no longer exists
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgremove("testVG", None)
-
-class LvmTestVGrename(LvmPVVGTestCase):
-    def test_vgrename(self):
-        """Verify that it is possible to rename a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # try rename
-        succ = BlockDev.lvm_vgrename("testVG", "testVG_new", None)
-        self.assertTrue(succ)
-
-        # rename back
-        succ = BlockDev.lvm_vgrename("testVG_new", "testVG", None)
-        self.assertTrue(succ)
-
-        # (hopefully) non-existing VG
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgrename("testVG_new", "testVG", None)
-
-class LvmTestVGactivateDeactivate(LvmPVVGTestCase):
-    def test_vgactivate_vgdeactivate(self):
-        """Verify that it is possible to (de)activate a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgactivate("nonexistingVG", None)
-
-        succ = BlockDev.lvm_vgactivate("testVG", None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgdeactivate("nonexistingVG", None)
-
-        succ = BlockDev.lvm_vgdeactivate("testVG", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgactivate("testVG", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgdeactivate("testVG", None)
-        self.assertTrue(succ)
-
-class LvmTestVGextendReduce(LvmPVVGTestCase):
-    def test_vgextend_vgreduce(self):
-        """Verify that it is possible to extend/reduce a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgextend("nonexistingVG", self.loop_dev2, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgextend("testVG", "/non/existing/device", None)
-
-        succ = BlockDev.lvm_vgextend("testVG", self.loop_dev2, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgreduce("testVG", self.loop_dev, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgextend("testVG", self.loop_dev, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgreduce("testVG", self.loop_dev2, None)
-        self.assertTrue(succ)
-
-        # try to remove missing PVs (there are none)
-        succ = BlockDev.lvm_vgreduce("testVG", None, None)
-        self.assertTrue(succ)
-
-class LvmTestVGinfo(LvmPVVGTestCase):
-    def test_vginfo(self):
-        """Verify that it is possible to gather info about a VG"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.name, "testVG")
-        self.assertTrue(info.uuid)
-        self.assertEqual(info.pv_count, 2)
-        self.assertLess(info.size, 2 * 1024**3)
-        self.assertEqual(info.free, info.size)
-        self.assertEqual(info.extent_size, 4 * 1024**2)
-
-class LvmTestVGs(LvmPVVGTestCase):
-    def test_vgs(self):
-        """Verify that it's possible to gather info about VGs"""
-
-        vgs = BlockDev.lvm_vgs()
-        orig_len = len(vgs)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        vgs = BlockDev.lvm_vgs()
-        self.assertGreater(len(vgs), orig_len)
-        self.assertTrue(any(info.name == "testVG" for info in vgs))
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-
-        self.assertTrue(any(info.uuid == all_info.uuid for all_info in vgs))
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgremove("nonexistingVG", None)
-
-        succ = BlockDev.lvm_vgremove("testVG", None)
-        self.assertTrue(succ)
-
-        # already removed
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vgremove("testVG", None)
-
-        succ = BlockDev.lvm_pvremove(self.loop_dev, None)
-        self.assertTrue(succ)
-
-class LvmTestVGLocking(LvmPVVGTestCase):
-    @tag_test(TestTags.UNSAFE)
-    def test_vglock_stop_start(self):
-        """Verify that it is possible to start and stop locking on a VG"""
-
-        # better not do anything if lvmlockd is running, shared VGs have
-        # a tendency to wreak havoc on your system if you look at them wrong
-        ret, _out, _err = run_command("systemctl is-active lvmlockd")
-        if ret == 0:
-            self.skipTest("lvmlockd is running, skipping")
-
-        _ret, out, _err = run_command("lvm config 'global/use_lvmlockd'")
-        if "use_lvmlockd=0" not in out:
-            self.skipTest("lvmlockd is enabled, skipping")
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # this actually doesn't "test" anything, the commands will just say lvmlockd is not
-        # running and return 0, but that's good enough for us
-        succ = BlockDev.lvm_vglock_start("testVG")
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vglock_stop("testVG")
-        self.assertTrue(succ)
-
-class LvmTestPVTags(LvmPVVGTestCase):
-    def test_pvtags(self):
-        """Verify that it's possible to set and get info about PV tags"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        # only pvs in a vg can be tagged so we need a vg here
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertFalse(info.pv_tags)
-
-        succ = BlockDev.lvm_add_pv_tags(self.loop_dev, ["a", "b", "c"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_tags, ["a", "b", "c"])
-
-        succ = BlockDev.lvm_delete_pv_tags(self.loop_dev, ["a", "b"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_tags, ["c"])
-
-        succ = BlockDev.lvm_add_pv_tags(self.loop_dev, ["e"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev)
-        self.assertTrue(info)
-        self.assertEqual(info.pv_tags, ["c", "e"])
-
-class LvmTestVGTags(LvmPVVGTestCase):
-    def test_vgtags(self):
-        """Verify that it's possible to set and get info about VG tags"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertFalse(info.vg_tags)
-
-        succ = BlockDev.lvm_add_vg_tags("testVG", ["a", "b", "c"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.vg_tags, ["a", "b", "c"])
-
-        succ = BlockDev.lvm_delete_vg_tags("testVG", ["a", "b"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.vg_tags, ["c"])
-
-        succ = BlockDev.lvm_add_vg_tags("testVG", ["e"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertEqual(info.vg_tags, ["c", "e"])
-
-class LvmPVVGLVTestCase(LvmPVVGTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        except:
-            pass
-
-        LvmPVVGTestCase._clean_up(self)
-
-class LvmTestLVcreateRemove(LvmPVVGLVTestCase):
-    @tag_test(TestTags.CORE)
-    def test_lvcreate_lvremove(self):
-        """Verify that it's possible to create/destroy an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("nonexistingVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, ["/non/existing/device"], None)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # not enough space (only one PV)
-        with self.assertRaisesRegex(GLib.GError, "Insufficient free space"):
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 1048 * 1024**2, None, [self.loop_dev], None)
-
-        # enough space (two PVs)
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 1048 * 1024**2, None, [self.loop_dev, self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("nonexistingVG", "testLV", True, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("testVG", "nonexistingLV", True, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("nonexistingVG", "nonexistingLV", True, None)
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # already removed
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-
-class LvmTestPartialLVs(LvmPVVGLVTestCase):
-    # the mirror halves are actually written to during sync-up and the
-    # default sparse_size of 1Gig is too much for a regular /tmp, so
-    # let's use smaller ones here.
-    #
-    _sparse_size = 20*1024**2
-
-    @tag_test(TestTags.CORE)
-    def test_lvpartial(self):
-        """Verify that missing PVs are detected and can be dealt with"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev3, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2, self.loop_dev3], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_pvinfo(self.loop_dev2)
-        self.assertTrue(info)
-        self.assertFalse(info.missing)
-        self.assertEqual(info.vg_name, "testVG")
-        loop_dev2_pv_uuid = info.pv_uuid
-
-        # Create a mirrored LV on the first two PVs
-        with wait_for_sync("testVG", "testLV"):
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 5 * 1024**2, "raid1",
-                                         [self.loop_dev, self.loop_dev2], None)
-            self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.attr[8], "-")
-
-        # Check that lvs_tree returns the expected structure
-
-        def assert_lv_subs(info, segtype, len_segs, len_data, len_metadata):
-            self.assertTrue(info)
-            self.assertEqual(info.segtype, segtype)
-            self.assertEqual(len(info.segs), len_segs)
-            self.assertEqual(len(info.data_lvs), len_data)
-            self.assertEqual(len(info.metadata_lvs), len_metadata)
-
-        def assert_lv_single_pv(info, pv):
-            if pv:
-                assert_lv_subs(info, "linear", 1, 0, 0)
-                self.assertEqual(info.segs[0].pvdev, pv)
-            else:
-                assert_lv_subs(info, "linear", 0, 0, 0)
-
-        def assert_raid1_structure(pv1, pv2):
-            lvs = { lv.lv_name: lv for lv in BlockDev.lvm_lvs_tree("testVG") }
-            info = lvs["testLV"]
-            assert_lv_subs(info, "raid1", 0, 2, 2)
-            assert_lv_single_pv(lvs["["+info.data_lvs[0]+"]"], pv1)
-            assert_lv_single_pv(lvs["["+info.data_lvs[1]+"]"], pv2)
-            assert_lv_single_pv(lvs["["+info.metadata_lvs[0]+"]"], pv1)
-            assert_lv_single_pv(lvs["["+info.metadata_lvs[1]+"]"], pv2)
-
-        assert_raid1_structure(self.loop_dev, self.loop_dev2)
-
-        # Disconnect the second PV, this should cause it to be flagged
-        # as missing, and testLV to be reported as "partial".
-        delete_lio_device(self.loop_dev2)
-
-        pvs = BlockDev.lvm_pvs()
-        found = False
-        for pv in pvs:
-            if pv.pv_uuid == loop_dev2_pv_uuid:
-                found = True
-                self.assertTrue(pv.missing)
-                self.assertEqual(pv.vg_name, "testVG")
-        self.assertTrue(found)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.attr[8], "p")
-
-        # lvs_tree should report the second stripe to be missing
-        assert_raid1_structure(self.loop_dev, None)
-
-        # remove records of missing PVs
-        succ = BlockDev.lvm_vgreduce("testVG", None, None)
-        self.assertTrue(succ)
-
-        pvs = BlockDev.lvm_pvs()
-        found = False
-        for pv in pvs:
-            if pv.pv_uuid == loop_dev2_pv_uuid:
-                found = True
-        self.assertFalse(found)
-
-        # lvs_tree should still report the second stripe to be missing
-        assert_raid1_structure(self.loop_dev, None)
-
-        # repair testLV with the third PV
-        with wait_for_sync("testVG", "testLV"):
-            succ = BlockDev.lvm_lvrepair("testVG", "testLV", [self.loop_dev3])
-            self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.attr[8], "-")
-
-        assert_raid1_structure(self.loop_dev, self.loop_dev3)
-
-class LvmTestLVcreateWithExtra(LvmPVVGLVTestCase):
-    def __init__(self, *args, **kwargs):
-        LvmPVVGLVTestCase.__init__(self, *args, **kwargs)
-        self.log = ""
-        self.ignore_log = True
-
-    @classmethod
-    def setUpClass(cls):
-        # we are checking for info log messages and default level is warning
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_INFO)
-
-        super(LvmTestLVcreateWithExtra, cls).setUpClass()
-
-    @classmethod
-    def tearDownClass(cls):
-        # reset back to default
-        BlockDev.utils_set_log_level(BlockDev.UTILS_LOG_WARNING)
-
-        super(LvmTestLVcreateWithExtra, cls).tearDownClass()
-
-    def my_log_func(self, level, msg):
-        if self.ignore_log:
-            return
-        # not much to verify here
-        self.assertTrue(isinstance(level, int))
-        self.assertTrue(isinstance(msg, str))
-
-        self.log += msg + "\n"
-
-    def test_lvcreate_with_extra(self):
-        """Verify that it's possible to create an LV with extra arguments"""
-
-        self.ignore_log = True
-        self.assertTrue(BlockDev.reinit(self.requested_plugins, False, self.my_log_func))
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("nonexistingVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, ["/non/existing/device"], None)
-
-        self.ignore_log = False
-        ea = BlockDev.ExtraArg.new("-Z", "y")
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], [ea])
-        self.assertTrue(succ)
-        match = re.match(r".*lvcreate.*-Z y.*", self.log)
-        self.assertIsNot(match, None)
-
-        self.assertTrue(BlockDev.reinit(self.requested_plugins, False, None))
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-class LvmTestLVcreateType(LvmPVVGLVTestCase):
-    _sparse_size = 200 * 1024**2
-
-    def test_lvcreate_type(self):
-        """Verify it's possible to create LVs with various types"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # try to create a striped LV
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "striped", [self.loop_dev, self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        # verify that the LV has the requested segtype
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.segtype, "striped")
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        with wait_for_sync("testVG", "testLV"):
-            # try to create a mirrored LV
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "mirror", [self.loop_dev, self.loop_dev2], None)
-            self.assertTrue(succ)
-
-        # verify that the LV has the requested segtype
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.segtype, "mirror")
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        with wait_for_sync("testVG", "testLV"):
-            # try to create a raid1 LV
-            succ = BlockDev.lvm_lvcreate("testVG", "testLV", 100 * 1024**2, "raid1", [self.loop_dev, self.loop_dev2], None)
-            self.assertTrue(succ)
-
-        # verify that the LV has the requested segtype
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertEqual(info.segtype, "raid1")
-
-        succ = BlockDev.lvm_lvremove("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-
-class LvmTestLVactivateDeactivate(LvmPVVGLVTestCase):
-    def test_lvactivate_lvdeactivate(self):
-        """Verify it's possible to (de)actiavate an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvactivate("nonexistingVG", "testLV", True)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvactivate("testVG", "nonexistingLV", True)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvactivate("nonexistingVG", "nonexistingLV", True)
-
-        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvdeactivate("nonexistingVG", "testLV", None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvdeactivate("testVG", "nonexistingLV", None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvdeactivate("nonexistingVG", "nonexistingLV", None)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-        # try activating in shared mode, unfortunately no way to check whether it really
-        # works or not
-        succ = BlockDev.lvm_lvactivate("testVG", "testLV", True, True)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-class LvmTestLVresize(LvmPVVGLVTestCase):
-    def test_lvresize(self):
-        """Verify that it's possible to resize an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvresize("nonexistingVG", "testLV", 768 * 1024**2, None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvresize("testVG", "nonexistingLV", 768 * 1024**2, None)
-
-        # grow
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 768 * 1024**2, None)
-        self.assertTrue(succ)
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.LVM), "libbd_lvm.so.3")
 
-        # same size
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvresize("testVG", "testLV", 768 * 1024**2, None)
-
-        # shrink
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 512 * 1024**2, None)
-        self.assertTrue(succ)
-
-        # shrink, not a multiple of 512
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 500 * 1024**2, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvdeactivate("testVG", "testLV", None)
-        self.assertTrue(succ)
-
-        # try to shrink when deactivated
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 400 * 1024**2, None)
-        self.assertTrue(succ)
-
-class LvmTestLVrename(LvmPVVGLVTestCase):
-    def test_lvrename(self):
-        """Verify that it's possible to rename an LV"""
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvrename("nonexistingVG", "testLV", "newTestLV", None)
-
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvrename("testVG", "nonexistingLV", "newTestLV", None)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        # rename
-        succ = BlockDev.lvm_lvrename("testVG", "testLV", "newTestLV", None)
-        self.assertTrue(succ)
-
-        # and back
-        succ = BlockDev.lvm_lvrename("testVG", "newTestLV", "testLV", None)
-        self.assertTrue(succ)
-
-        # needs a change
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_lvrename("testVG", "testLV", "testLV", None)
-
-class LvmTestLVsnapshots(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_snapshotcreate_lvorigin_snapshotmerge(self):
-        """Verify that LV snapshot support works"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvsnapshotcreate("testVG", "testLV", "testLV_bak", 256 * 1024**2, None)
-        self.assertTrue(succ)
-
-        origin_name = BlockDev.lvm_lvorigin("testVG", "testLV_bak")
-        lvi = BlockDev.lvm_lvinfo("testVG", "testLV_bak")
-        self.assertEqual(origin_name, "testLV")
-        self.assertEqual(lvi.origin, "testLV")
-        self.assertIn("snapshot", lvi.roles.split(","))
-
-        succ = BlockDev.lvm_lvsnapshotmerge("testVG", "testLV_bak", None)
-        self.assertTrue(succ)
-
-class LvmTestLVinfo(LvmPVVGLVTestCase):
-    def test_lvinfo(self):
-        """Verify that it is possible to gather info about an LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_name, "testLV")
-        self.assertEqual(info.vg_name, "testVG")
-        self.assertTrue(info.uuid)
-        self.assertEqual(info.size, 512 * 1024**2)
-        self.assertIn("public", info.roles.split(","))
-
-class LvmTestLVs(LvmPVVGLVTestCase):
-    def test_lvs(self):
-        """Verify that it's possible to gather info about LVs"""
-
-        lvs = BlockDev.lvm_lvs(None)
-        orig_len = len(lvs)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs(None)
-        self.assertGreater(len(lvs), orig_len)
-        self.assertTrue(any(info.lv_name == "testLV" and info.vg_name == "testVG" for info in lvs))
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-
-        self.assertTrue(any(info.uuid == all_info.uuid for all_info in lvs))
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 1)
-
-class LvmTestLVsMultiSegment(LvmPVVGLVTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testLV2", True, None)
-        except:
-            pass
-
-        LvmPVVGLVTestCase._clean_up(self)
-
-    def test_lvs(self):
-        """Verify that it's possible to gather info about LVs"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 10 * 1024**2)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 1)
-        self.assertListEqual([lv.lv_name for lv in lvs], ["testLV"])
-
-        # the LV will have a single segment on loop_dev
-        info = BlockDev.lvm_lvinfo_tree("testVG", "testLV")
-        self.assertEqual(info.segtype, "linear")
-        self.assertEqual(len(info.segs), 1)
-        self.assertEqual(info.segs[0].pvdev, self.loop_dev)
-
-        # add second LV
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV2", 10 * 1024**2)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 2)
-        self.assertListEqual([lv.lv_name for lv in lvs], ["testLV", "testLV2"])
-
-        # by resizing the first LV we will create two segments
-        succ = BlockDev.lvm_lvresize("testVG", "testLV", 20 * 1024**2, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo_tree("testVG", "testLV")
-        self.assertEqual(info.segtype, "linear")
-        self.assertEqual(len(info.segs), 2)
-        self.assertEqual(info.segs[0].pvdev, self.loop_dev)
-        self.assertEqual(info.segs[1].pvdev, self.loop_dev)
-        self.assertNotEqual(info.segs[0].pv_start_pe, info.segs[1].pv_start_pe)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertEqual(len(lvs), 2)
-        self.assertListEqual([lv.lv_name for lv in lvs], ["testLV", "testLV2"])
-
-class LvmPVVGthpoolTestCase(LvmPVVGTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testPool", True, None)
-        except:
-            pass
-
-        LvmPVVGTestCase._clean_up(self)
-
-class LvmTestLVsAll(LvmPVVGthpoolTestCase):
-    def test_lvs_all(self):
-        """Verify that info is gathered for all LVs"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        # there should be at least 3 LVs -- testPool, [testPool_tdata], [testPool_tmeta] (plus probably some spare LVs)
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertGreater(len(lvs), 3)
-
-class LvmTestThpoolCreate(LvmPVVGthpoolTestCase):
-    @tag_test(TestTags.CORE)
-    def test_thpoolcreate(self):
-        """Verify that it is possible to create a thin pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-        self.assertIn("private", info.roles.split(","))
-
-
-class LvmTestThpoolConvert(LvmPVVGthpoolTestCase):
-    def test_thpool_convert(self):
-        """Verify that it is possible to create a thin pool by conversion"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        # the name of the data LV is used for the pool
-        succ = BlockDev.lvm_lvcreate("testVG", "dataLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_lvcreate("testVG", "metadataLV", 50 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpool_convert("testVG", "dataLV", "metadataLV", "testPool", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-
-
-class LvmTestDataMetadataLV(LvmPVVGthpoolTestCase):
-    def test_data_metadata_lv_name(self):
-        """Verify that it is possible to get name of the data/metadata LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        lvi = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertTrue(lvi.data_lv)
-        self.assertTrue(lvi.data_lv.startswith("testPool"))
-        self.assertIn("_tdata", lvi.data_lv)
-
-        info = BlockDev.lvm_lvinfo("testVG", lvi.data_lv)
-        self.assertTrue(info.attr.startswith("T"))
-        self.assertIn("private", info.roles.split(","))
-        self.assertIn("data", info.roles.split(","))
-
-        lvi = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertTrue(lvi.metadata_lv)
-        self.assertTrue(lvi.metadata_lv.startswith("testPool"))
-        self.assertIn("_tmeta", lvi.metadata_lv)
-
-        info = BlockDev.lvm_lvinfo("testVG", lvi.metadata_lv)
-        self.assertTrue(info.attr.startswith("e"))
-        self.assertIn("private", info.roles.split(","))
-        self.assertIn("metadata", info.roles.split(","))
-
-class LvmPVVGLVthLVTestCase(LvmPVVGthpoolTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testThLV", True, None)
-        except:
-            pass
-
-        LvmPVVGthpoolTestCase._clean_up(self)
-
-class LvmTestThLVcreate(LvmPVVGLVthLVTestCase):
-    @tag_test(TestTags.CORE)
-    def test_thlvcreate_thpoolname(self):
-        """Verify that it is possible to create a thin LV and get its pool name"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, None, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thlvcreate("testVG", "testPool", "testThLV", 1024**3, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testThLV")
-        self.assertIn("V", info.attr)
-
-        pool = BlockDev.lvm_thlvpoolname("testVG", "testThLV")
-        lvi = BlockDev.lvm_lvinfo("testVG", "testThLV")
-        self.assertEqual(pool, "testPool")
-        self.assertEqual(lvi.pool_lv, "testPool")
-
-class LvmPVVGLVthLVsnapshotTestCase(LvmPVVGLVthLVTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testThLV_bak", True, None)
-        except:
-            pass
-
-        LvmPVVGLVthLVTestCase._clean_up(self)
-
-class LvmTestThSnapshotCreate(LvmPVVGLVthLVsnapshotTestCase):
-    def test_thsnapshotcreate(self):
-        """Verify that it is possible to create a thin LV snapshot"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, None, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thlvcreate("testVG", "testPool", "testThLV", 1024**3, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testPool")
-        self.assertIn("t", info.attr)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testThLV")
-        self.assertIn("V", info.attr)
-
-        succ = BlockDev.lvm_thsnapshotcreate("testVG", "testThLV", "testThLV_bak", "testPool", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testThLV_bak")
-        self.assertIn("V", info.attr)
-        self.assertIn("snapshot", info.roles.split(","))
-        self.assertIn("thinsnapshot", info.roles.split(","))
-
-class LvmPVVGLVcachePoolTestCase(LvmPVVGLVTestCase):
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVG", "testCache", True, None)
-        except:
-            pass
-
-        # lets help udev with removing stale symlinks
-        try:
-            if not BlockDev.lvm_lvs("testVG") and os.path.exists("/dev/testVG/testCache_meta"):
-                shutil.rmtree("/dev/testVG", ignore_errors=True)
-        except:
-            pass
-
-        LvmPVVGLVTestCase._clean_up(self)
-
-class LvmPVVGLVcachePoolCreateRemoveTestCase(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW, TestTags.UNSTABLE)
-    def test_cache_pool_create_remove(self):
-        """Verify that is it possible to create and remove a cache pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvremove("testVG", "testCache", True, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITEBACK,
-                                              BlockDev.LVMCachePoolFlags.STRIPED|BlockDev.LVMCachePoolFlags.META_RAID1,
-                                              [self.loop_dev, self.loop_dev2])
-        self.assertTrue(succ)
-
-class LvmTestCachePoolConvert(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_pool_convert(self):
-        """Verify that it is possible to create a cache pool by conversion"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "dataLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-        succ = BlockDev.lvm_lvcreate("testVG", "metadataLV", 50 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_pool_convert("testVG", "dataLV", "metadataLV", "testCache", None)
-        self.assertTrue(succ)
-
-
-class LvmPVVGLVcachePoolAttachDetachTestCase(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_pool_attach_detach(self):
-        """Verify that is it possible to attach and detach a cache pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        # detach and destroy (the last arg)
-        succ = BlockDev.lvm_cache_detach("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # once more and do not destroy this time
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_detach("testVG", "testLV", False, None)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertTrue(any(info.lv_name == "testCache" for info in lvs))
-
-class LvmPVVGcachedLVTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_create_cached_lv(self):
-        """Verify that it is possible to create a cached LV in a single step"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_cached_lv("testVG", "testLV", 512 * 1024**2, 256 * 1024**2, 10 * 1024**2,
-                                                   BlockDev.LVMCacheMode.WRITEBACK, 0,
-                                                   [self.loop_dev], [self.loop_dev2])
-        self.assertTrue(succ)
-
-class LvmPVVGcachedLVpoolTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_get_pool_name(self):
-        """Verify that it is possible to get the name of the cache pool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        lvm_version = self._get_lvm_version()
-        if lvm_version < Version("2.03.06"):
-            cpool_name = "testCache"
-        else:
-            # since 2.03.06 LVM adds _cpool suffix to the cache pool after attaching it
-            cpool_name = "testCache_cpool"
-
-        self.assertEqual(BlockDev.lvm_cache_pool_name("testVG", "testLV"), cpool_name)
-
-class LvmPVVGcachedLVstatsTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_get_stats(self):
-        """Verify that it is possible to get stats for a cached LV"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        stats = BlockDev.lvm_cache_stats("testVG", "testLV")
-        self.assertTrue(stats)
-        self.assertEqual(stats.cache_size, 512 * 1024**2)
-        self.assertEqual(stats.md_size, 8 * 1024**2)
-        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
-
-class LvmPVVGcachedThpoolstatsTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_cache_get_stats(self):
-        """Verify that it is possible to get stats for a cached thinpool"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_create_pool("testVG", "testCache", 512 * 1024**2, 0, BlockDev.LVMCacheMode.WRITETHROUGH, 0, [self.loop_dev2])
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_thpoolcreate("testVG", "testPool", 512 * 1024**2, 4 * 1024**2, 512 * 1024, "thin-performance", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_cache_attach("testVG", "testPool", "testCache", None)
-        self.assertTrue(succ)
-
-        # just ask for the pool itself even if it's not technically cached
-        stats = BlockDev.lvm_cache_stats("testVG", "testPool")
-        self.assertTrue(stats)
-        self.assertEqual(stats.cache_size, 512 * 1024**2)
-        self.assertEqual(stats.md_size, 8 * 1024**2)
-        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
-
-        # same should work when explicitly asking for the data LV
-        stats = BlockDev.lvm_cache_stats("testVG", "testPool_tdata")
-        self.assertTrue(stats)
-        self.assertEqual(stats.cache_size, 512 * 1024**2)
-        self.assertEqual(stats.md_size, 8 * 1024**2)
-        self.assertEqual(stats.mode, BlockDev.LVMCacheMode.WRITETHROUGH)
-
-
-class LvmPVVGLVWritecacheAttachDetachTestCase(LvmPVVGLVcachePoolTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_writecache_attach_detach(self):
-        """Verify that is it possible to attach and detach a writecache LV"""
-
-        lvm_version = self._get_lvm_version()
-        if lvm_version < Version("2.03.02"):
-            self.skipTest("LVM writecache support not available")
-
-        lvm_segtypes = self._get_lvm_segtypes()
-        if "writecache" not in lvm_segtypes:
-            self.skipTest("LVM writecache support not available")
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testCache", 512 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertIsNotNone(info)
-        self.assertEqual(info.segtype, "writecache")
-
-        # detach and destroy (the last arg)
-        succ = BlockDev.lvm_writecache_detach("testVG", "testLV", True, None)
-        self.assertTrue(succ)
-
-        # once more and do not destroy this time
-        succ = BlockDev.lvm_lvcreate("testVG", "testCache", 512 * 1024**2, None, [self.loop_dev2], None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_attach("testVG", "testLV", "testCache", None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_detach("testVG", "testLV", False, None)
-        self.assertTrue(succ)
-
-        lvs = BlockDev.lvm_lvs("testVG")
-        self.assertTrue(any(info.lv_name == "testCache" for info in lvs))
-
-class LvmPVVGWritecachedLVTestCase(LvmPVVGLVTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_create_cached_lv(self):
-        """Verify that it is possible to create a writecached LV in a single step"""
-
-        lvm_version = self._get_lvm_version()
-        if lvm_version < Version("2.03.02"):
-            self.skipTest("LVM writecache support not available")
-
-        lvm_segtypes = self._get_lvm_segtypes()
-        if "writecache" not in lvm_segtypes:
-            self.skipTest("LVM writecache support not available")
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_writecache_create_cached_lv("testVG", "testLV", 512 * 1024**2, 256 * 1024**2,
-                                                        [self.loop_dev], [self.loop_dev2])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertIsNotNone(info)
-        self.assertEqual(info.segtype, "writecache")
-
-class LvmVGExportedTestCase(LvmPVVGLVTestCase):
-
-    def _clean_up(self):
-        run_command("vgimport testVG")
-
-        LvmPVVGLVTestCase._clean_up(self)
-
-    @tag_test(TestTags.SLOW)
-    def test_exported_vg(self):
-        """Verify that info has correct information about exported VGs"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev2, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev, self.loop_dev2], 0, None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertFalse(info.exported)
-
-        ret, out, err = run_command("vgexport testVG")
-        if ret != 0:
-            self.fail("Failed to export VG:\n%s %s" % (out, err))
-
-        info = BlockDev.lvm_vginfo("testVG")
-        self.assertTrue(info)
-        self.assertTrue(info.exported)
-
-
-        self.assertTrue(succ)
-
-class LvmTestLVTags(LvmPVVGLVTestCase):
-    def test_vgtags(self):
-        """Verify that it's possible to set and get info about LV tags"""
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_lvcreate("testVG", "testLV", 512 * 1024**2, None, [self.loop_dev], None)
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertFalse(info.lv_tags)
-
-        succ = BlockDev.lvm_add_lv_tags("testVG", "testLV", ["a", "b", "c"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_tags, ["a", "b", "c"])
-
-        succ = BlockDev.lvm_delete_lv_tags("testVG", "testLV", ["a", "b"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_tags, ["c"])
-
-        succ = BlockDev.lvm_add_lv_tags("testVG", "testLV", ["e"])
-        self.assertTrue(succ)
-
-        info = BlockDev.lvm_lvinfo("testVG", "testLV")
-        self.assertTrue(info)
-        self.assertEqual(info.lv_tags, ["c", "e"])
-
-
-class LVMTechTest(LVMTestCase):
-
-    @tag_test(TestTags.NOSTORAGE)
     def test_tech_available(self):
         """Verify that checking lvm tool availability by technology works as expected"""
 
@@ -1967,324 +57,73 @@ class LVMTechTest(LVMTestCase):
         avail = BlockDev.lvm_is_tech_avail(BlockDev.LVMTech.BASIC, BlockDev.LVMTechMode.CREATE)
         self.assertTrue(avail)
 
-class LVMVDOTest(LVMTestCase):
-
-    loop_size = 8 * 1024**3
 
+class LvmVDOTest(_lvm_cases.LvmVDOTest, LvmTestCase):
     @classmethod
     def setUpClass(cls):
-        if not BlockDev.utils_have_kernel_module("dm-vdo"):
-            raise unittest.SkipTest("VDO kernel module not available, skipping.")
-
-        try:
-            BlockDev.utils_load_kernel_module("dm-vdo")
-        except GLib.GError as e:
-            if "File exists" not in e.message:
-                raise unittest.SkipTest("cannot load VDO kernel module, skipping.")
-
-        lvm_version = cls._get_lvm_version()
-        if lvm_version < Version("2.3.07"):
-            raise unittest.SkipTest("LVM version 2.3.07 or newer needed for LVM VDO.")
-
-        super().setUpClass()
-
-    def setUp(self):
-        self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("vdo_test", self.loop_size)
-        try:
-            self.loop_dev = create_lio_device(self.dev_file)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-
-        succ = BlockDev.lvm_pvcreate(self.loop_dev, 0, 0, None)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vgcreate("testVDOVG", [self.loop_dev], 0, None)
-        self.assertTrue(succ)
+        _lvm_cases.LvmVDOTest.setUpClass()
+        LvmTestCase.setUpClass()
 
-    def _clean_up(self):
-        try:
-            BlockDev.lvm_lvremove("testVDOVG", "vdoPool", True, None)
-        except:
-            pass
-
-        BlockDev.lvm_vgremove("testVDOVG")
-        BlockDev.lvm_pvremove(self.loop_dev)
-
-        # XXX remove lingering /dev entries
-        shutil.rmtree("/dev/testVDOVG", ignore_errors=True)
-
-        try:
-            delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
-
-    @tag_test(TestTags.SLOW, TestTags.CORE)
-    def test_vdo_pool_create(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
-        self.assertIsNotNone(lv_info)
-        self.assertEqual(lv_info.segtype, "vdo")
-        self.assertEqual(lv_info.pool_lv, "vdoPool")
-
-        pool_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool")
-        self.assertEqual(pool_info.segtype, "vdo-pool")
-        self.assertEqual(pool_info.data_lv, "vdoPool_vdata")
-        lvm_version = self._get_lvm_version()
-        if lvm_version >= Version("2.03.24"):
-            self.assertGreater(pool_info.data_percent, 0)
-
-        pool = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV")
-        self.assertEqual(pool, lv_info.pool_lv)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertEqual(vdo_info.operating_mode, BlockDev.LVMVDOOperatingMode.NORMAL)
-        self.assertEqual(vdo_info.compression_state, BlockDev.LVMVDOCompressionState.ONLINE)
-        self.assertTrue(vdo_info.compression)
-        self.assertTrue(vdo_info.deduplication)
-        self.assertGreater(vdo_info.index_memory_size, 0)
-        self.assertGreater(vdo_info.used_size, 0)
-        self.assertTrue(0 <= vdo_info.saving_percent <= 100)
-
-        lvs = BlockDev.lvm_lvs("testVDOVG")
-        self.assertIn("vdoPool", [l.lv_name for l in lvs])
-        self.assertIn("vdoLV", [l.lv_name for l in lvs])
-
-        mode_str = BlockDev.lvm_get_vdo_operating_mode_str(vdo_info.operating_mode)
-        self.assertEqual(mode_str, "normal")
-
-        state_str = BlockDev.lvm_get_vdo_compression_state_str(vdo_info.compression_state)
-        self.assertEqual(state_str, "online")
-
-        policy_str = BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy)
-        self.assertIn(policy_str, ["sync", "async", "auto"])
-
-    @tag_test(TestTags.SLOW)
-    def test_vdo_pool_create_options(self):
-        # set index size to 300 MiB, disable compression and write policy to sync
-        policy = BlockDev.lvm_get_vdo_write_policy_from_str("sync")
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3,
-                                            300 * 1024**2, False, True, policy)
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertEqual(vdo_info.index_memory_size, 300 * 1024**2)
-        self.assertFalse(vdo_info.compression)
-        self.assertTrue(vdo_info.deduplication)
-        self.assertEqual(BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy), "sync")
-
-    @tag_test(TestTags.SLOW)
-    def test_vdo_pool_create_noname(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", None, 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
-        self.assertIsNotNone(lv_info)
-        self.assertEqual(lv_info.segtype, "vdo")
-
-        pool_name = BlockDev.lvm_vdolvpoolname("testVDOVG", "vdoLV")
-        self.assertEqual(lv_info.pool_lv, pool_name)
-        pool_info = BlockDev.lvm_lvinfo("testVDOVG", pool_name)
-        self.assertEqual(pool_info.segtype, "vdo-pool")
-
-    @tag_test(TestTags.SLOW)
-    def test_resize(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 5 * 1024**3, 10 * 1024**3)
-        self.assertTrue(succ)
-
-        # "physical" resize first (pool), shrinking not allowed
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_vdo_pool_resize("testVDOVG", "vdoPool", 4 * 1024**3)
-
-        succ = BlockDev.lvm_vdo_pool_resize("testVDOVG", "vdoPool", 7 * 1024**3)
-        self.assertTrue(succ)
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoPool")
-        self.assertEqual(lv_info.size, 7 * 1024**3)
-
-        # "logical" resize (LV)
-        succ = BlockDev.lvm_vdo_resize("testVDOVG", "vdoLV", 35 * 1024**3)
-        self.assertTrue(succ)
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
-        self.assertEqual(lv_info.size, 35 * 1024**3)
-
-    @tag_test(TestTags.SLOW)
-    def test_enable_disable_compression(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.compression)
-
-        # disable
-        succ = BlockDev.lvm_vdo_disable_compression("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertFalse(vdo_info.compression)
-
-        # enable
-        succ = BlockDev.lvm_vdo_enable_compression("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.compression)
-
-    @tag_test(TestTags.SLOW)
-    def test_enable_disable_deduplication(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.deduplication)
-
-        # disable
-        succ = BlockDev.lvm_vdo_disable_deduplication("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertFalse(vdo_info.deduplication)
-
-        # enable
-        succ = BlockDev.lvm_vdo_enable_deduplication("testVDOVG", "vdoPool")
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.deduplication)
-
-    @tag_test(TestTags.SLOW)
-    def test_vdo_pool_convert(self):
-        succ = BlockDev.lvm_lvcreate("testVDOVG", "testLV", 7 * 1024**3)
-        self.assertTrue(succ)
-
-        succ = BlockDev.lvm_vdo_pool_convert("testVDOVG", "testLV", "vdoLV", 35 * 1024**3)
-        self.assertTrue(succ)
-
-        lv_info = BlockDev.lvm_lvinfo("testVDOVG", "vdoLV")
-        self.assertIsNotNone(lv_info)
-        self.assertEqual(lv_info.size, 35 * 1024**3)
-        self.assertEqual(lv_info.segtype, "vdo")
-        self.assertEqual(lv_info.pool_lv, "testLV")
-
-        pool_info = BlockDev.lvm_lvinfo("testVDOVG", "testLV")
-        self.assertIsNotNone(pool_info)
-        self.assertEqual(pool_info.segtype, "vdo-pool")
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "testLV")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.compression)
-        self.assertTrue(vdo_info.deduplication)
-        self.assertEqual(BlockDev.lvm_get_vdo_write_policy_str(vdo_info.write_policy), "auto")
-
-    @tag_test(TestTags.SLOW)
-    def test_stats(self):
-        succ = BlockDev.lvm_vdo_pool_create("testVDOVG", "vdoLV", "vdoPool", 7 * 1024**3, 35 * 1024**3)
-        self.assertTrue(succ)
-
-        vdo_info = BlockDev.lvm_vdo_info("testVDOVG", "vdoPool")
-        self.assertIsNotNone(vdo_info)
-        self.assertTrue(vdo_info.deduplication)
-
-        vdo_stats = BlockDev.lvm_vdo_get_stats("testVDOVG", "vdoPool")
-
-        lvm_version = self._get_lvm_version()
-        if lvm_version >= Version("2.03.24"):
-            # saving_percent is incorrect with LVM < 2.03.24
-            self.assertEqual(vdo_info.saving_percent, vdo_stats.saving_percent)
-
-        # just sanity check
-        self.assertNotEqual(vdo_stats.used_percent, -1)
-        self.assertNotEqual(vdo_stats.block_size, -1)
-        self.assertNotEqual(vdo_stats.logical_block_size, -1)
-        self.assertNotEqual(vdo_stats.physical_blocks, -1)
-        self.assertNotEqual(vdo_stats.data_blocks_used, -1)
-        self.assertNotEqual(vdo_stats.overhead_blocks_used, -1)
-        self.assertNotEqual(vdo_stats.logical_blocks_used, -1)
-        self.assertNotEqual(vdo_stats.write_amplification_ratio, -1)
-
-        full_stats = BlockDev.lvm_vdo_get_stats_full("testVDOVG", "vdoPool")
-        self.assertIn("writeAmplificationRatio", full_stats.keys())
-
-
-class LvmTestDevicesFile(LvmPVonlyTestCase):
-    devicefile = "bd_lvm_test.devices"
 
+class LvmTestPVs(_lvm_cases.LvmTestPVs, LvmTestCase):
     @classmethod
-    def tearDownClass(cls):
-        try:
-            os.remove("/etc/lvm/devices/" + cls.devicefile)
-        except FileNotFoundError:
-            pass
-
-        super(LvmTestDevicesFile, cls).tearDownClass()
-
-    def test_devices_add_delete(self):
-        if not self.devices_avail:
-            self.skipTest("skipping LVM devices file test: not supported")
-
-        self.addCleanup(BlockDev.lvm_set_global_config, None)
+    def setUpClass(cls):
+        _lvm_cases.LvmTestPVs.setUpClass()
+        LvmTestCase.setUpClass()
 
-        # force-enable the feature, it might be disabled by default
-        succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=1 }")
-        self.assertTrue(succ)
 
-        succ = BlockDev.lvm_pvcreate(self.loop_dev)
-        self.assertTrue(succ)
+class LvmTestVGs(_lvm_cases.LvmTestVGs, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestVGs.setUpClass()
+        LvmTestCase.setUpClass()
 
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_devices_add("/non/existing/device", self.devicefile)
 
-        with self.assertRaises(GLib.GError):
-            BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
+class LvmTestLVs(_lvm_cases.LvmTestLVs, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestLVs.setUpClass()
+        LvmTestCase.setUpClass()
 
-        succ = BlockDev.lvm_devices_add(self.loop_dev, self.devicefile)
-        self.assertTrue(succ)
 
-        dfile = read_file("/etc/lvm/devices/" + self.devicefile)
-        self.assertIn(self.loop_dev, dfile)
+class LvmCLITestLVcreateType(_lvm_cases.LvmPVVGLVTestCase, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmPVVGLVTestCase.setUpClass()
+        LvmTestCase.setUpClass()
 
-        succ = BlockDev.lvm_devices_delete(self.loop_dev, self.devicefile)
-        self.assertTrue(succ)
 
-        dfile = read_file("/etc/lvm/devices/" + self.devicefile)
-        self.assertNotIn(self.loop_dev, dfile)
+class LvmTestPartialLVs(_lvm_cases.LvmTestPartialLVs, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestPartialLVs.setUpClass()
+        LvmTestCase.setUpClass()
 
-        BlockDev.lvm_set_global_config(None)
 
-    def test_devices_enabled(self):
-        if not self.devices_avail:
-            self.skipTest("skipping LVM devices file test: not supported")
+class LvmTestThpool(_lvm_cases.LvmTestThpool, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestThpool.setUpClass()
+        LvmTestCase.setUpClass()
 
-        self.addCleanup(BlockDev.lvm_set_global_config, None)
 
-        # checking if the feature is enabled or disabled is hard so lets just disable
-        # the devices file using the global config and check lvm_devices_add fails
-        # with the correct exception message
-        succ = BlockDev.lvm_set_global_config("devices { use_devicesfile=0 }")
-        self.assertTrue(succ)
+class LvmTestThLV(_lvm_cases.LvmTestThLV, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestThLV.setUpClass()
+        LvmTestCase.setUpClass()
 
-        with self.assertRaisesRegex(GLib.GError, "LVM devices file not enabled."):
-            BlockDev.lvm_devices_add("", self.devicefile)
 
+class LvmTestCache(_lvm_cases.LvmTestCache, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestCache.setUpClass()
+        LvmTestCase.setUpClass()
 
-class LvmConfigTestPvremove(LvmPVonlyTestCase):
 
-    @tag_test(TestTags.REGRESSION)
-    def test_set_empty_config(self):
-        succ = BlockDev.lvm_pvcreate(self.loop_dev)
-        self.assertTrue(succ)
+class LvmTestDevicesFile(_lvm_cases.LvmTestDevicesFile, LvmTestCase):
+    @classmethod
+    def setUpClass(cls):
+        _lvm_cases.LvmTestDevicesFile.setUpClass()
+        LvmTestCase.setUpClass()
 
-        BlockDev.lvm_set_global_config("")
-        succ = BlockDev.lvm_pvremove(self.loop_dev)
-        self.assertTrue(succ)
diff -pruN 3.3.1-3/tests/mdraid_test.py 3.4.0-1/tests/mdraid_test.py
--- 3.3.1-3/tests/mdraid_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/mdraid_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -42,18 +42,9 @@ class MDTest(unittest.TestCase):
 
 class MDNoDevTestCase(MDTest):
 
-    requested_plugins = BlockDev.plugin_specs_from_names(("mdraid",))
-
-    @classmethod
-    def setUpClass(cls):
-        if not BlockDev.is_initialized():
-            BlockDev.init(cls.requested_plugins, None)
-        else:
-            BlockDev.reinit(cls.requested_plugins, True, None)
-
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.MDRAID), "libbd_mdraid.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.MDRAID), "libbd_mdraid.so.3")
 
     @tag_test(TestTags.NOSTORAGE)
     def test_get_superblock_size(self):
@@ -100,28 +91,25 @@ class MDNoDevTestCase(MDTest):
 class MDTestCase(MDTest):
 
     _sparse_size = 10 * 1024**2
+    _num_devices = 2
+    loop_devs = []
+    dev_files = []
 
     def setUp(self):
         if os.uname()[-1] == "i686":
             self.skipTest("Skipping hanging MD RAID tests on i686")
 
         self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("md_test", self._sparse_size)
-        self.dev_file2 = create_sparse_tempfile("md_test", self._sparse_size)
-        self.dev_file3 = create_sparse_tempfile("md_test", self._sparse_size)
 
-        try:
-            self.loop_dev = create_lio_device(self.dev_file)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev2 = create_lio_device(self.dev_file2)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev3 = create_lio_device(self.dev_file3)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
+        for i in range(self._num_devices):
+            dev_file = create_sparse_tempfile("md_test", self._sparse_size)
+            self.dev_files.append(dev_file)
+
+            try:
+                loop_dev = create_lio_device(self.dev_files[i])
+                self.loop_devs.append(loop_dev)
+            except RuntimeError as e:
+                raise RuntimeError("Failed to setup loop device for testing: %s" % e)
 
     def _clean_up(self):
         try:
@@ -132,18 +120,13 @@ class MDTestCase(MDTest):
             BlockDev.md_deactivate(BlockDev.md_node_from_name("bd_test_md"))
         except:
             pass
-        try:
-            BlockDev.md_destroy(self.loop_dev)
-        except:
-            pass
-        try:
-            BlockDev.md_destroy(self.loop_dev2)
-        except:
-            pass
-        try:
-            BlockDev.md_destroy(self.loop_dev3)
-        except:
-            pass
+
+        for i in range(self._num_devices):
+            try:
+                BlockDev.md_destroy(self.loop_devs[i])
+            except:
+                pass
+
         try:
             BlockDev.md_deactivate("bd_test_md")
         except:
@@ -153,26 +136,16 @@ class MDTestCase(MDTest):
         except:
             pass
 
-        try:
-            delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
-
-        try:
-            delete_lio_device(self.loop_dev2)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file2)
+        for i in range(self._num_devices):
+            try:
+                delete_lio_device(self.loop_devs[i])
+            except RuntimeError:
+                # just move on, we can do no better here
+                pass
+            os.unlink(self.dev_files[i])
 
-        try:
-            delete_lio_device(self.loop_dev3)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file3)
+        self.dev_files.clear()
+        self.loop_devs.clear()
 
 
 class MDTestCreateDeactivateDestroy(MDTestCase):
@@ -182,13 +155,13 @@ class MDTestCreateDeactivateDestroy(MDTe
 
         with self.assertRaises(GLib.GError):
             BlockDev.md_create("bd_test_md2", "raid1",
-                               ["/non/existing/device", self.loop_dev2],
-                               1, None, None)
+                               ["/non/existing/device", self.loop_devs[1]],
+                               0, None, None)
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, None)
+                                      [self.loop_devs[0], self.loop_devs[1]],
+                                      0, None, None)
             self.assertTrue(succ)
 
         # newly created array should be 'clean'
@@ -198,13 +171,12 @@ class MDTestCreateDeactivateDestroy(MDTe
         succ = BlockDev.md_deactivate("bd_test_md")
         self.assertTrue(succ)
 
-        succ = BlockDev.md_destroy(self.loop_dev)
+        succ = BlockDev.md_destroy(self.loop_devs[0])
         self.assertTrue(succ)
-        succ = BlockDev.md_destroy(self.loop_dev2)
-        self.assertTrue(succ)
-        succ = BlockDev.md_destroy(self.loop_dev3)
+        succ = BlockDev.md_destroy(self.loop_devs[1])
         self.assertTrue(succ)
 
+
 class MDTestCreateWithChunkSize(MDTestCase):
     @tag_test(TestTags.SLOW)
     def test_create_with_chunk_size(self):
@@ -212,146 +184,117 @@ class MDTestCreateWithChunkSize(MDTestCa
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid0",
-                                      [self.loop_dev, self.loop_dev2],
+                                      [self.loop_devs[0], self.loop_devs[1]],
                                       0, None, None, 512 * 1024)
             self.assertTrue(succ)
 
-        ex_data = BlockDev.md_examine(self.loop_dev)
+        ex_data = BlockDev.md_examine(self.loop_devs[0])
         self.assertEqual(ex_data.chunk_size, 512 * 1024)
 
         succ = BlockDev.md_deactivate("bd_test_md")
         self.assertTrue(succ)
 
-        succ = BlockDev.md_destroy(self.loop_dev)
+        succ = BlockDev.md_destroy(self.loop_devs[0])
         self.assertTrue(succ)
-        succ = BlockDev.md_destroy(self.loop_dev2)
-        self.assertTrue(succ)
-        succ = BlockDev.md_destroy(self.loop_dev3)
+        succ = BlockDev.md_destroy(self.loop_devs[1])
         self.assertTrue(succ)
 
+
 class MDTestActivateDeactivate(MDTestCase):
     @tag_test(TestTags.SLOW, TestTags.CORE)
     def test_activate_deactivate(self):
         """Verify that it is possible to activate and deactivate an MD RAID"""
 
-        with wait_for_action("resync"):
-            succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, None)
-            self.assertTrue(succ)
+        succ = BlockDev.md_create("bd_test_md", "raid0",
+                                    [self.loop_devs[0], self.loop_devs[1]],
+                                    0, None, None)
+        self.assertTrue(succ)
 
         with self.assertRaises(GLib.GError):
             BlockDev.md_deactivate("non_existing_md")
 
-        with wait_for_action("resync"):
-            succ = BlockDev.md_deactivate("bd_test_md")
-            self.assertTrue(succ)
+        succ = BlockDev.md_deactivate("bd_test_md")
+        self.assertTrue(succ)
 
         with self.assertRaises(GLib.GError):
             BlockDev.md_activate("bd_test_md",
-                                 ["/non/existing/device", self.loop_dev2, self.loop_dev3], None)
+                                 ["/non/existing/device", self.loop_devs[1]], None)
 
-        with wait_for_action("resync"):
-            succ = BlockDev.md_activate("bd_test_md",
-                                        [self.loop_dev, self.loop_dev2, self.loop_dev3], None)
-            self.assertTrue(succ)
+        succ = BlockDev.md_activate("bd_test_md",
+                                    [self.loop_devs[0], self.loop_devs[1]], None)
+        self.assertTrue(succ)
 
         # try to activate again, should not fail, just no-op
         succ = BlockDev.md_activate("bd_test_md",
-                                    [self.loop_dev, self.loop_dev2, self.loop_dev3], None)
+                                    [self.loop_devs[0], self.loop_devs[1]], None)
         self.assertTrue(succ)
 
         # try to deactivate using the node instead of name
-        with wait_for_action("resync"):
-            succ = BlockDev.md_deactivate(BlockDev.md_node_from_name("bd_test_md"))
-            self.assertTrue(succ)
+        succ = BlockDev.md_deactivate(BlockDev.md_node_from_name("bd_test_md"))
+        self.assertTrue(succ)
 
         # try to activate using full path, not just the name
         # (it should work too and blivet does this)
-        with wait_for_action("resync"):
-            succ = BlockDev.md_activate("/dev/md/bd_test_md",
-                                        [self.loop_dev, self.loop_dev2, self.loop_dev3], None)
-            self.assertTrue(succ)
-
-class MDTestActivateWithUUID(MDTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_activate_with_uuid(self):
-        """Verify that it is possible to activate an MD RAID with UUID"""
-
-        with wait_for_action("resync"):
-            succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, None)
-            self.assertTrue(succ)
+        succ = BlockDev.md_activate("/dev/md/bd_test_md",
+                                    [self.loop_devs[0], self.loop_devs[1]], None)
+        self.assertTrue(succ)
 
-        with wait_for_action("resync"):
-            succ = BlockDev.md_deactivate("bd_test_md")
-            self.assertTrue(succ)
+        succ = BlockDev.md_deactivate("bd_test_md")
+        self.assertTrue(succ)
 
-        md_info = BlockDev.md_examine(self.loop_dev)
+        md_info = BlockDev.md_examine(self.loop_devs[0])
         self.assertTrue(md_info)
         self.assertTrue(md_info.uuid)
 
-        with wait_for_action("resync"):
-            succ = BlockDev.md_activate("bd_test_md", [self.loop_dev, self.loop_dev2, self.loop_dev3], md_info.uuid)
-
-class MDTestActivateByUUID(MDTestCase):
-    @tag_test(TestTags.SLOW)
-    def test_activate_by_uuid(self):
-        """Verify that it is possible to activate an MD RAID by UUID"""
-
-        with wait_for_action("resync"):
-            succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, None)
-            self.assertTrue(succ)
-
-        with wait_for_action("resync"):
-            succ = BlockDev.md_deactivate("bd_test_md")
-            self.assertTrue(succ)
+        # try to activate with UUID and array name
+        succ = BlockDev.md_activate("bd_test_md", [self.loop_devs[0], self.loop_devs[1]], md_info.uuid)
+        self.assertTrue(succ)
 
-        md_info = BlockDev.md_examine(self.loop_dev)
-        self.assertTrue(md_info)
-        self.assertTrue(md_info.uuid)
+        succ = BlockDev.md_deactivate("bd_test_md")
+        self.assertTrue(succ)
 
-        # should work with member devices specified
-        with wait_for_action("resync"):
-            succ = BlockDev.md_activate(None, [self.loop_dev, self.loop_dev2, self.loop_dev3], md_info.uuid)
+        # try to activate by UUID only: should work with member devices specified
+        succ = BlockDev.md_activate(None, [self.loop_devs[0], self.loop_devs[1]], md_info.uuid)
+        self.assertTrue(succ)
 
-        with wait_for_action("resync"):
-            succ = BlockDev.md_deactivate("bd_test_md")
-            self.assertTrue(succ)
+        succ = BlockDev.md_deactivate("bd_test_md")
+        self.assertTrue(succ)
 
         # as well as without them
-        with wait_for_action("resync"):
-            succ = BlockDev.md_activate(None, None, md_info.uuid)
+        succ = BlockDev.md_activate(None, None, md_info.uuid)
+        self.assertTrue(succ)
+
+        succ = BlockDev.md_deactivate("bd_test_md")
+        self.assertTrue(succ)
 
 
 class MDTestNominateDenominate(MDTestCase):
+    _num_devices = 3
+
     @tag_test(TestTags.SLOW)
     def test_nominate_denominate(self):
         """Verify that it is possible to nominate and denominate an MD RAID device"""
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
+                                      [self.loop_devs[0], self.loop_devs[1], self.loop_devs[2]],
                                       1, None, None)
             self.assertTrue(succ)
 
         with wait_for_action("resync"):
-            succ = BlockDev.md_denominate(self.loop_dev)
+            succ = BlockDev.md_denominate(self.loop_devs[0])
             self.assertTrue(succ)
 
         with wait_for_action("resync"):
-            succ = BlockDev.md_nominate(self.loop_dev)
+            succ = BlockDev.md_nominate(self.loop_devs[0])
             self.assertTrue(succ)
 
         with wait_for_action("resync"):
-            succ = BlockDev.md_denominate(self.loop_dev)
+            succ = BlockDev.md_denominate(self.loop_devs[0])
             self.assertTrue(succ)
 
         with wait_for_action("resync"):
-            succ = BlockDev.md_nominate(self.loop_dev)
+            succ = BlockDev.md_nominate(self.loop_devs[0])
             self.assertTrue(succ)
 
         with wait_for_action("resync"):
@@ -360,17 +303,19 @@ class MDTestNominateDenominate(MDTestCas
 
 
 class MDTestAddRemove(MDTestCase):
+    _num_devices = 3
+
     @tag_test(TestTags.SLOW)
     def test_add_remove(self):
         """Verify that it is possible to add a device to and remove from an MD RAID"""
 
         # the MD array doesn't exist yet
         with self.assertRaises(GLib.GError):
-            BlockDev.md_add("bd_test_md", self.loop_dev3, 0, None)
+            BlockDev.md_add("bd_test_md", self.loop_devs[2], 0, None)
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2],
+                                      [self.loop_devs[0], self.loop_devs[1]],
                                       0, None, None)
             self.assertTrue(succ)
 
@@ -378,7 +323,7 @@ class MDTestAddRemove(MDTestCase):
             BlockDev.md_add("bd_test_md", "/non/existing/device", 0, None)
 
         # add the device as a spare
-        succ = BlockDev.md_add("bd_test_md", self.loop_dev3, 0, None)
+        succ = BlockDev.md_add("bd_test_md", self.loop_devs[2], 0, None)
         self.assertTrue(succ)
 
         md_info = BlockDev.md_detail("bd_test_md")
@@ -386,11 +331,11 @@ class MDTestAddRemove(MDTestCase):
         self.assertEqual(md_info.spare_devices, 1)
 
         with self.assertRaises(GLib.GError):
-            BlockDev.md_add("bd_test_md", self.loop_dev3, 0, None)
+            BlockDev.md_add("bd_test_md", self.loop_devs[2], 0, None)
 
         # now remove the spare device (should be possible without --fail)
         with wait_for_action("resync"):
-            succ = BlockDev.md_remove("bd_test_md", self.loop_dev3, False, None)
+            succ = BlockDev.md_remove("bd_test_md", self.loop_devs[2], False, None)
             self.assertTrue(succ)
 
         md_info = BlockDev.md_detail("bd_test_md")
@@ -399,7 +344,7 @@ class MDTestAddRemove(MDTestCase):
 
         # remove one of the original devices (with --fail enabled)
         with wait_for_action("resync"):
-            succ = BlockDev.md_remove("bd_test_md", self.loop_dev2, True, None)
+            succ = BlockDev.md_remove("bd_test_md", self.loop_devs[1], True, None)
             self.assertTrue(succ)
 
         md_info = BlockDev.md_detail("bd_test_md")
@@ -410,7 +355,7 @@ class MDTestAddRemove(MDTestCase):
         # now try to add it back -- it should be re-added automatically as
         # a RAID device, not a spare device
         with wait_for_action("recovery"):
-            succ = BlockDev.md_add("bd_test_md", self.loop_dev2, 0, None)
+            succ = BlockDev.md_add("bd_test_md", self.loop_devs[1], 0, None)
             self.assertTrue(succ)
 
         md_info = BlockDev.md_detail("bd_test_md")
@@ -418,19 +363,21 @@ class MDTestAddRemove(MDTestCase):
         self.assertEqual(md_info.active_devices, 2)
         self.assertEqual(md_info.spare_devices, 0)
 
+
 class MDTestExamineDetail(MDTestCase):
-    # sleeps to let MD RAID sync things
+    _num_devices = 3
+
     @tag_test(TestTags.SLOW)
     def test_examine_detail(self):
         """Verify that it is possible to get info about an MD RAID"""
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
+                                      [self.loop_devs[0], self.loop_devs[1], self.loop_devs[2]],
                                       1, None, None)
             self.assertTrue(succ)
 
-        ex_data = BlockDev.md_examine(self.loop_dev)
+        ex_data = BlockDev.md_examine(self.loop_devs[0])
         # test that we got something
         self.assertTrue(ex_data)
 
@@ -475,6 +422,7 @@ class MDTestExamineDetail(MDTestCase):
         de_data = BlockDev.md_detail("/dev/%s" % node)
         self.assertTrue(de_data)
 
+
 class MDTestNameNodeBijection(MDTestCase):
     @tag_test(TestTags.SLOW)
     def test_name_node_bijection(self):
@@ -482,8 +430,8 @@ class MDTestNameNodeBijection(MDTestCase
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, None)
+                                      [self.loop_devs[0], self.loop_devs[1]],
+                                      0, None, None)
             self.assertTrue(succ)
 
         node = BlockDev.md_node_from_name("bd_test_md")
@@ -496,16 +444,15 @@ class MDTestNameNodeBijection(MDTestCase
         with self.assertRaisesRegex(GLib.GError, r'No name'):
             BlockDev.md_name_from_node("no_such_node")
 
-        succ = BlockDev.md_deactivate("bd_test_md");
+        succ = BlockDev.md_deactivate("bd_test_md")
         self.assertTrue(succ)
 
-        succ = BlockDev.md_destroy(self.loop_dev)
-        self.assertTrue(succ)
-        succ = BlockDev.md_destroy(self.loop_dev2)
+        succ = BlockDev.md_destroy(self.loop_devs[0])
         self.assertTrue(succ)
-        succ = BlockDev.md_destroy(self.loop_dev3)
+        succ = BlockDev.md_destroy(self.loop_devs[1])
         self.assertTrue(succ)
 
+
 class MDTestSetBitmapLocation(MDTestCase):
     @tag_test(TestTags.SLOW, TestTags.UNSTABLE)
     def test_set_bitmap_location(self):
@@ -513,8 +460,8 @@ class MDTestSetBitmapLocation(MDTestCase
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, "none")
+                                      [self.loop_devs[0], self.loop_devs[1]],
+                                      0, None, "none")
             self.assertTrue(succ)
 
         loc = BlockDev.md_get_bitmap_location("bd_test_md")
@@ -524,8 +471,8 @@ class MDTestSetBitmapLocation(MDTestCase
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, "internal")
+                                      [self.loop_devs[0], self.loop_devs[1]],
+                                      0, None, "internal")
             self.assertTrue(succ)
 
         loc = BlockDev.md_get_bitmap_location("bd_test_md")
@@ -576,8 +523,8 @@ class MDTestRequestSyncAction(MDTestCase
 
         with wait_for_action("resync"):
             succ = BlockDev.md_create("bd_test_md", "raid1",
-                                      [self.loop_dev, self.loop_dev2, self.loop_dev3],
-                                      1, None, None)
+                                      [self.loop_devs[0], self.loop_devs[1]],
+                                      0, None, None)
             self.assertTrue(succ)
 
         with wait_for_action("check"):
@@ -607,7 +554,7 @@ class MDTestDDFRAID(MDTestCase):
 
     def test_examine_ddf_container(self):
         succ = BlockDev.md_create("bd_test_md", "container",
-                                  [self.loop_dev, self.loop_dev2],
+                                  [self.loop_devs[0], self.loop_devs[1]],
                                   0, "ddf", None)
         self.assertTrue(succ)
 
@@ -615,7 +562,7 @@ class MDTestDDFRAID(MDTestCase):
         ret, _out, err = run_command("mdadm --create /dev/md/bd_test_ddf --run --level=raid0 --raid-devices=2 /dev/md/bd_test_md")
         self.assertEqual(ret, 0, msg="Failed to create RAID for DDF test: %s" % err)
 
-        edata = BlockDev.md_examine(self.loop_dev)
+        edata = BlockDev.md_examine(self.loop_devs[0])
         self.assertIsNotNone(edata)
         self.assertIsNotNone(edata.uuid)
         self.assertEqual(edata.level, "container")
diff -pruN 3.3.1-3/tests/mpath_test.py 3.4.0-1/tests/mpath_test.py
--- 3.3.1-3/tests/mpath_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/mpath_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -51,7 +51,7 @@ class MpathTestCase(MpathTest):
 class MpathNoDevTestCase(MpathTest):
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.MPATH), "libbd_mpath.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.MPATH), "libbd_mpath.so.3")
 
     @tag_test(TestTags.NOSTORAGE)
     def test_get_mpath_members(self):
diff -pruN 3.3.1-3/tests/nvme_test.py 3.4.0-1/tests/nvme_test.py
--- 3.3.1-3/tests/nvme_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/nvme_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -25,7 +25,7 @@ class NVMeTest(unittest.TestCase):
             raise unittest.SkipTest("nvme executable (nvme-cli package) not found in $PATH, skipping.")
         if not shutil.which("nvmetcli"):
             raise unittest.SkipTest("nvmetcli executable not found in $PATH, skipping.")
-        ret, out, err = run_command("modprobe nvme-fabrics")
+        ret, _out, _err = run_command("modprobe nvme-fabrics")
         if ret != 0:
             raise unittest.SkipTest("nvme-fabrics kernel module unavailable, skipping.")
 
@@ -44,7 +44,7 @@ class NVMeTest(unittest.TestCase):
 class NVMePluginVersionTestCase(NVMeTest):
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.NVME), "libbd_nvme.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.NVME), "libbd_nvme.so.3")
 
     @tag_test(TestTags.NOSTORAGE)
     def test_availability(self):
@@ -283,7 +283,7 @@ class NVMeTestCase(NVMeTest):
         with self.assertRaisesRegex(GLib.GError, r".*Failed to open device .*': No such file or directory"):
             BlockDev.nvme_get_sanitize_log("/dev/nonexistent")
 
-        message = r"NVMe Get Log Page - Sanitize Status Log command error: Invalid Field in Command: A reserved coded value or an unsupported value in a defined field|NVMe Get Log Page - Sanitize Status Log command error: unrecognized"
+        message = r"NVMe Get Log Page - Sanitize Status Log command error: (Invalid Field in Command: A reserved coded value or an unsupported value in a defined field|unrecognized|No such file or directory)"
         with self.assertRaisesRegex(GLib.GError, message):
             # Cannot retrieve sanitize log on a nvme target loop devices
             BlockDev.nvme_get_sanitize_log(self.nvme_dev)
@@ -416,7 +416,7 @@ class NVMeFabricsTestCase(NVMeTest):
         NUM_NS = 3
 
         # test that no device node exists for given subsystem nqn
-        ctrls = find_nvme_ctrl_devs_for_subnqn(self.SUBNQN)
+        ctrls = find_nvme_ctrl_devs_for_subnqn(self.SUBNQN, wait_for_ready=False)
         self.assertEqual(len(ctrls), 0)
 
         self._setup_target(NUM_NS)
@@ -514,13 +514,11 @@ class NVMeFabricsTestCase(NVMeTest):
             self.addCleanup(write_file, HOSTNQN_PATH, saved_hostnqn)
         except:
             self.addCleanup(self._safe_unlink, HOSTNQN_PATH)
-            pass
         try:
             saved_hostid = read_file(HOSTID_PATH)
             self.addCleanup(write_file, HOSTID_PATH, saved_hostid)
         except:
             self.addCleanup(self._safe_unlink, HOSTID_PATH)
-            pass
 
         self._safe_unlink(HOSTNQN_PATH)
         self._safe_unlink(HOSTID_PATH)
diff -pruN 3.3.1-3/tests/part_test.py 3.4.0-1/tests/part_test.py
--- 3.3.1-3/tests/part_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/part_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -14,10 +14,9 @@ from gi.repository import GLib, BlockDev
 
 
 @required_plugins(("part",))
-class PartTestCase(unittest.TestCase):
+class PartTest(unittest.TestCase):
 
     requested_plugins = BlockDev.plugin_specs_from_names(("part",))
-    block_size = 512
 
     @classmethod
     def _get_fdisk_version(cls):
@@ -34,42 +33,58 @@ class PartTestCase(unittest.TestCase):
         else:
             BlockDev.reinit(cls.requested_plugins, True, None)
 
+
+class PartTestCase(PartTest):
+    block_size = 512
+    _sparse_size = 100 * 1024**2
+    _num_devices = 1
+    loop_devs = []
+    dev_files = []
+
     def setUp(self):
         self.addCleanup(self._clean_up)
-        self.dev_file = create_sparse_tempfile("part_test", 100 * 1024**2)
-        self.dev_file2 = create_sparse_tempfile("part_test", 100 * 1024**2)
-        try:
-            self.loop_dev = create_lio_device(self.dev_file, self.block_size)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
-        try:
-            self.loop_dev2 = create_lio_device(self.dev_file2)
-        except RuntimeError as e:
-            raise RuntimeError("Failed to setup loop device for testing: %s" % e)
+
+        for i in range(self._num_devices):
+            dev_file = create_sparse_tempfile("part_test", self._sparse_size)
+            self.dev_files.append(dev_file)
+
+            try:
+                loop_dev = create_lio_device(self.dev_files[i], self.block_size)
+                self.loop_devs.append(loop_dev)
+            except RuntimeError as e:
+                raise RuntimeError("Failed to setup loop device for testing: %s" % e)
 
     def _clean_up(self):
-        try:
-            delete_lio_device(self.loop_dev)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file)
+        for i in range(self._num_devices):
+            try:
+                delete_lio_device(self.loop_devs[i])
+            except RuntimeError:
+                # just move on, we can do no better here
+                pass
+            os.unlink(self.dev_files[i])
 
-        try:
-            delete_lio_device(self.loop_dev2)
-        except RuntimeError:
-            # just move on, we can do no better here
-            pass
-        os.unlink(self.dev_file2)
+        self.dev_files.clear()
+        self.loop_devs.clear()
 
 
-class PartCPluginVersionCase(PartTestCase):
+class PartNoDevTestCase(PartTest):
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.PART), "libbd_part.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.PART), "libbd_part.so.3")
+
+    @tag_test(TestTags.NOSTORAGE)
+    def test_part_type_str(self):
+        types = {BlockDev.PartType.NORMAL: 'primary', BlockDev.PartType.LOGICAL: 'logical',
+                 BlockDev.PartType.EXTENDED: 'extended', BlockDev.PartType.FREESPACE: 'free',
+                 BlockDev.PartType.METADATA: 'metadata', BlockDev.PartType.PROTECTED: 'primary'}
+
+        for key, value in types.items():
+            self.assertEqual(BlockDev.part_get_type_str(key), value)
 
 
 class PartCreateTableCase(PartTestCase):
+    _num_devices = 2
+
     @tag_test(TestTags.CORE)
     def test_create_table(self):
         """Verify that it is possible to create a new partition table"""
@@ -84,21 +99,21 @@ class PartCreateTableCase(PartTestCase):
 
         # doesn't matter if we want to ignore any preexisting partition tables
         # or not on a clean device
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, False)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, False)
         self.assertTrue(succ)
 
-        succ = BlockDev.part_create_table (self.loop_dev2, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[1], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # should fail because of a preexisting partition table (and not ignoring it)
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, False)
+            BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, False)
 
         # should succeed if we want to ignore any preexisting partition tables
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
 
@@ -114,29 +129,29 @@ class PartGetDiskSpecCase(PartTestCase):
         with self.assertRaises(GLib.GError):
             BlockDev.part_get_disk_spec ("/non/existing/device")
 
-        ps = BlockDev.part_get_disk_spec (self.loop_dev)
+        ps = BlockDev.part_get_disk_spec (self.loop_devs[0])
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev)
+        self.assertEqual(ps.path, self.loop_devs[0])
         self.assertEqual(ps.sector_size, self.block_size)
         self.assertGreaterEqual(ps.size, 100 * 1024**2 - 512)
         self.assertEqual(ps.table_type, BlockDev.PartTableType.UNDEF)
 
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
-        ps = BlockDev.part_get_disk_spec (self.loop_dev)
+        ps = BlockDev.part_get_disk_spec (self.loop_devs[0])
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev)
+        self.assertEqual(ps.path, self.loop_devs[0])
         self.assertEqual(ps.sector_size, self.block_size)
         self.assertGreaterEqual(ps.size, 100 * 1024**2 - 512)
         self.assertEqual(ps.table_type, BlockDev.PartTableType.MSDOS)
 
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
-        ps = BlockDev.part_get_disk_spec (self.loop_dev)
+        ps = BlockDev.part_get_disk_spec (self.loop_devs[0])
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev)
+        self.assertEqual(ps.path, self.loop_devs[0])
         self.assertEqual(ps.sector_size, self.block_size)
         self.assertGreaterEqual(ps.size, 100 * 1024**2 - 512)
         self.assertEqual(ps.table_type, BlockDev.PartTableType.GPT)
@@ -152,28 +167,28 @@ class PartCreatePartCase(PartTestCase):
         """Verify that it is possible to create a partition"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 16 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 16 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 16 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should get proper data back
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * self.block_size)
         self.assertEqual(ps.size, 16 * 1024**2)
         self.assertEqual(ps.id, "0x83")  # default ID "linux filesystem"
 
-        ps2 = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps2 = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.path, ps2.path)
-        self.assertEqual(ps.type, ps2.type);
+        self.assertEqual(ps.type, ps2.type)
         self.assertEqual(ps.start, ps2.start)
         self.assertEqual(ps.size, ps2.size)
 
-        pss = BlockDev.part_get_disk_parts (self.loop_dev)
+        pss = BlockDev.part_get_disk_parts (self.loop_devs[0])
         self.assertEqual(len(pss), 1)
         ps3 = pss[0]
         self.assertEqual(ps.path, ps3.path)
@@ -185,27 +200,27 @@ class PartCreatePartCase(PartTestCase):
         """Verify that it is possible to create a partition with minimal start and optimal alignment"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 16 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 1, 16 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 1, 16 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should get proper data back
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertLessEqual(ps.start, 2048 * self.block_size)
         self.assertEqual(ps.size, 16 * 1024**2)
 
-        ps2 = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps2 = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.path, ps2.path)
-        self.assertEqual(ps.type, ps2.type);
+        self.assertEqual(ps.type, ps2.type)
         self.assertEqual(ps.start, ps2.start)
         self.assertEqual(ps.size, ps2.size)
 
-        pss = BlockDev.part_get_disk_parts (self.loop_dev)
+        pss = BlockDev.part_get_disk_parts (self.loop_devs[0])
         self.assertEqual(len(pss), 1)
         ps3 = pss[0]
         self.assertEqual(ps.path, ps3.path)
@@ -217,27 +232,27 @@ class PartCreatePartCase(PartTestCase):
         """Verify that it is possible to create a partition with minimal start"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 2 MiB big with none alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 1, 2 * 1024**2, BlockDev.PartAlign.NONE)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 1, 2 * 1024**2, BlockDev.PartAlign.NONE)
 
         # we should get proper data back
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, self.block_size)
         self.assertEqual(ps.size, 2 * 1024**2)
 
-        ps2 = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps2 = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.path, ps2.path)
-        self.assertEqual(ps.type, ps2.type);
+        self.assertEqual(ps.type, ps2.type)
         self.assertEqual(ps.start, ps2.start)
         self.assertEqual(ps.size, ps2.size)
 
-        pss = BlockDev.part_get_disk_parts (self.loop_dev)
+        pss = BlockDev.part_get_disk_parts (self.loop_devs[0])
         self.assertEqual(len(pss), 1)
         ps3 = pss[0]
         self.assertEqual(ps.path, ps3.path)
@@ -254,69 +269,69 @@ class PartCreatePartFullCase(PartTestCas
     @tag_test(TestTags.CORE)
     def test_full_device_partition(self):
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # create partition spanning whole device even disregarding the partition table (loop_dev size)
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, 0, 100 * 1024**2, BlockDev.PartAlign.OPTIMAL)
-        succ = BlockDev.part_delete_part (self.loop_dev, ps.path)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, 0, 100 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_delete_part (self.loop_devs[0], ps.path)
         self.assertTrue(succ)
 
         # same, but create a maximal partition automatically
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, 0, 0, BlockDev.PartAlign.OPTIMAL)
-        succ = BlockDev.part_delete_part (self.loop_dev, ps.path)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, 0, 0, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_delete_part (self.loop_devs[0], ps.path)
         self.assertTrue(succ)
 
         # start at byte 1 and create partition spanning whole device explicitly
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, 1, 100 * 1024**2, BlockDev.PartAlign.OPTIMAL)
-        succ = BlockDev.part_delete_part (self.loop_dev, ps.path)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, 1, 100 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_delete_part (self.loop_devs[0], ps.path)
         self.assertTrue(succ)
 
         # start at byte 1 and create a maximal partition automatically
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, 1, 0, BlockDev.PartAlign.OPTIMAL)
-        succ = BlockDev.part_delete_part (self.loop_dev, ps.path)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, 1, 0, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_delete_part (self.loop_devs[0], ps.path)
         self.assertTrue(succ)
 
     def test_create_part_all_primary(self):
         """Verify that partition creation works as expected with all primary parts"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps2.start - (ps.start + ps.size + 1)), ps.start)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps2.start + ps2.size + 1,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps2.start + ps2.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps3.start - (ps2.start + ps2.size + 1)), ps.start)
         self.assertEqual(ps3.size, 10 * 1024**2)
 
-        ps4 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps3.start + ps3.size + 1,
+        ps4 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps3.start + ps3.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps4)
-        self.assertEqual(ps4.path, self.loop_dev + "4")
+        self.assertEqual(ps4.path, self.loop_devs[0] + "4")
         self.assertEqual(ps4.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -325,52 +340,52 @@ class PartCreatePartFullCase(PartTestCas
 
         # no more primary partitions allowed in the MSDOS table
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps4.start + ps4.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps4.start + ps4.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps4.start + ps4.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps4.start + ps4.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
     def test_create_part_with_extended(self):
         """Verify that partition creation works as expected with primary and extended parts"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps2.start - (ps.start + ps.size + 1)), ps.start)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps2.start + ps2.size + 1,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps2.start + ps2.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps3.start - (ps2.start + ps2.size + 1)), ps.start)
         self.assertEqual(ps3.size, 10 * 1024**2)
 
-        ps4 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps3.start + ps3.size + 1,
+        ps4 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps3.start + ps3.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps4)
-        self.assertEqual(ps4.path, self.loop_dev + "4")
+        self.assertEqual(ps4.path, self.loop_devs[0] + "4")
         self.assertEqual(ps4.type, BlockDev.PartType.EXTENDED)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -380,10 +395,10 @@ class PartCreatePartFullCase(PartTestCas
 
         # no more primary partitions allowed in the MSDOS table
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps4.start + ps4.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps4.start + ps4.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps4.start + ps4.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps4.start + ps4.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
     @tag_test(TestTags.CORE)
@@ -391,32 +406,32 @@ class PartCreatePartFullCase(PartTestCas
         """Verify that partition creation works as expected with primary, extended and logical parts"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps2.start - (ps.start + ps.size + 1)), ps.start)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps2.start + ps2.size + 1,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps2.start + ps2.size + 1,
                                          30 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.EXTENDED)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -425,30 +440,30 @@ class PartCreatePartFullCase(PartTestCas
 
         # the logical partition has number 5 even though the extended partition
         # has number 3
-        ps5 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps3.start + 1,
+        ps5 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps3.start + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps5)
-        self.assertEqual(ps5.path, self.loop_dev + "5")
+        self.assertEqual(ps5.path, self.loop_devs[0] + "5")
         self.assertEqual(ps5.type, BlockDev.PartType.LOGICAL)
         # the start has to be somewhere in the extended partition p3 which
         # should need at most 2 MiB extra space
         self.assertTrue(ps3.start < ps5.start < ps3.start + ps3.size)
         self.assertLess(abs(ps5.size - 10 * 1024**2), 2 * 1024**2)
 
-        ps6 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps5.start + ps5.size + 1,
+        ps6 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps5.start + ps5.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps6)
-        self.assertEqual(ps6.path, self.loop_dev + "6")
+        self.assertEqual(ps6.path, self.loop_devs[0] + "6")
         self.assertEqual(ps6.type, BlockDev.PartType.LOGICAL)
         # the start has to be somewhere in the extended partition p3 which
         # should need at most 2 MiB extra space
         self.assertTrue(ps3.start < ps6.start < ps3.start + ps3.size)
         self.assertEqual(ps6.size, 10 * 1024**2)
 
-        ps7 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps6.start + ps6.size + 2 * 1024**2,
+        ps7 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps6.start + ps6.size + 2 * 1024**2,
                                          5 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps7)
-        self.assertEqual(ps7.path, self.loop_dev + "7")
+        self.assertEqual(ps7.path, self.loop_devs[0] + "7")
         self.assertEqual(ps7.type, BlockDev.PartType.LOGICAL)
         # the start has to be somewhere in the extended partition p3 which
         # should need at most 2 MiB extra space
@@ -457,10 +472,10 @@ class PartCreatePartFullCase(PartTestCas
         self.assertEqual(ps7.size, 5 * 1024**2)
 
         # here we go with the partition number 4
-        ps4 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps3.start + ps3.size + 1,
+        ps4 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps3.start + ps3.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps4)
-        self.assertEqual(ps4.path, self.loop_dev + "4")
+        self.assertEqual(ps4.path, self.loop_devs[0] + "4")
         self.assertEqual(ps4.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -469,40 +484,40 @@ class PartCreatePartFullCase(PartTestCas
 
         # no more primary partitions allowed in the MSDOS table
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps3.start + ps3.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps3.start + ps3.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
     def test_create_part_with_extended_logical_gpt(self):
         """Verify that partition creation works as expected with primary, extended and logical parts on GPT"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * 512)
         self.assertEqual(ps.size, 10 * 1024**2)
         self.assertEqual(ps.attrs, 0)
 
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps2.start - (ps.start + ps.size + 1)), ps.start)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps2.start + ps2.size + 1,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps2.start + ps2.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -511,11 +526,11 @@ class PartCreatePartFullCase(PartTestCas
 
         # no extended partitions allowed in the GPT table
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps3.start + ps3.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps3.start + ps3.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         # no logical partitions allowed in the GPT table
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps3.start + ps3.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps3.start + ps3.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
     @tag_test(TestTags.CORE)
@@ -523,32 +538,32 @@ class PartCreatePartFullCase(PartTestCas
         """Verify that partition creation works as expected with the NEXT (auto) type"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps.start + ps.size + 1,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps.start + ps.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps2.start - (ps.start + ps.size + 1)), ps.start)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps2.start + ps2.size + 1,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps2.start + ps2.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -556,36 +571,36 @@ class PartCreatePartFullCase(PartTestCas
         self.assertEqual(ps3.size, 10 * 1024**2)
 
         # we should get a logical partition which has number 5
-        ps5 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps3.start + ps3.size + 1,
+        ps5 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps3.start + ps3.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
-        ps4 = BlockDev.part_get_part_spec (self.loop_dev, self.loop_dev + "4")
+        ps4 = BlockDev.part_get_part_spec (self.loop_devs[0], self.loop_devs[0] + "4")
         self.assertTrue(ps4)
-        self.assertEqual(ps4.path, self.loop_dev + "4")
+        self.assertEqual(ps4.path, self.loop_devs[0] + "4")
         self.assertEqual(ps4.type, BlockDev.PartType.EXTENDED)
         self.assertLess(abs(ps4.start - (ps3.start + ps3.size + 1)), ps.start)
         self.assertGreater(ps4.size, 65 * 1024**2)
 
         self.assertTrue(ps5)
-        self.assertEqual(ps5.path, self.loop_dev + "5")
+        self.assertEqual(ps5.path, self.loop_devs[0] + "5")
         self.assertEqual(ps5.type, BlockDev.PartType.LOGICAL)
         # the start has to be somewhere in the extended partition p4 no more
         # than 2 MiB after its start
         self.assertLessEqual(ps5.start, ps4.start + 2*1024**2)
         self.assertEqual(ps5.size, 10 * 1024**2)
 
-        ps6 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps5.start + ps5.size + 1,
+        ps6 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps5.start + ps5.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps6)
-        self.assertEqual(ps6.path, self.loop_dev + "6")
+        self.assertEqual(ps6.path, self.loop_devs[0] + "6")
         self.assertEqual(ps6.type, BlockDev.PartType.LOGICAL)
         # logical partitions start 1 MiB after each other (no idea why)
         self.assertLessEqual(abs(ps6.start - (ps5.start + ps5.size + 1)), 1024**2 + 512)
         self.assertEqual(ps6.size, 10 * 1024**2)
 
-        ps7 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps6.start + ps6.size + 1,
+        ps7 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps6.start + ps6.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps7)
-        self.assertEqual(ps7.path, self.loop_dev + "7")
+        self.assertEqual(ps7.path, self.loop_devs[0] + "7")
         self.assertEqual(ps7.type, BlockDev.PartType.LOGICAL)
         # logical partitions start 1 MiB after each other (no idea why)
         self.assertLessEqual(abs(ps7.start - (ps6.start + ps6.size + 1)), 1024**2 + 512)
@@ -594,10 +609,10 @@ class PartCreatePartFullCase(PartTestCas
         # no more primary nor extended partitions allowed in the MSDOS table and
         # there should be no space
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps4.start + ps4.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps4.start + ps4.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         with self.assertRaises(GLib.GError):
-            BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps4.start + ps4.size + 1,
+            BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps4.start + ps4.size + 1,
                                        10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
     @tag_test(TestTags.CORE)
@@ -605,32 +620,32 @@ class PartCreatePartFullCase(PartTestCas
         """Verify that partition creation works as expected with the NEXT (auto) type on GPT"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps.start + ps.size + 1,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps.start + ps.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps2.start - (ps.start + ps.size + 1)), ps.start)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps2.start + ps2.size + 1,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps2.start + ps2.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -638,28 +653,28 @@ class PartCreatePartFullCase(PartTestCas
         self.assertEqual(ps3.size, 10 * 1024**2)
 
         # we should get just next primary partition (GPT)
-        ps4 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps3.start + ps3.size + 1,
+        ps4 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps3.start + ps3.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps4)
-        self.assertEqual(ps4.path, self.loop_dev + "4")
+        self.assertEqual(ps4.path, self.loop_devs[0] + "4")
         self.assertEqual(ps4.type, BlockDev.PartType.NORMAL)
         self.assertLess(abs(ps4.start - (ps3.start + ps3.size + 1)), ps.start)
         self.assertEqual(ps4.size, 10 * 1024**2)
 
         # we should get just next primary partition (GPT)
-        ps5 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps4.start + ps4.size + 1,
+        ps5 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps4.start + ps4.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps5)
-        self.assertEqual(ps5.path, self.loop_dev + "5")
+        self.assertEqual(ps5.path, self.loop_devs[0] + "5")
         self.assertEqual(ps5.type, BlockDev.PartType.NORMAL)
         self.assertLess(abs(ps5.start - (ps4.start + ps4.size + 1)), ps.start)
         self.assertEqual(ps5.size, 10 * 1024**2)
 
         # we should get just next primary partition (GPT)
-        ps6 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NEXT, ps5.start + ps4.size + 1,
+        ps6 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NEXT, ps5.start + ps4.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps6)
-        self.assertEqual(ps6.path, self.loop_dev + "6")
+        self.assertEqual(ps6.path, self.loop_devs[0] + "6")
         self.assertEqual(ps6.type, BlockDev.PartType.NORMAL)
         self.assertLess(abs(ps6.start - (ps5.start + ps5.size + 1)), ps.start)
         self.assertEqual(ps6.size, 10 * 1024**2)
@@ -668,7 +683,7 @@ class PartGetDiskPartsCase(PartTestCase)
     def test_get_disk_parts_empty(self):
         """Verify that getting info about partitions with no label works"""
         with self.assertRaises(GLib.GError):
-            BlockDev.part_get_disk_parts (self.loop_dev)
+            BlockDev.part_get_disk_parts (self.loop_devs[0])
 
 
 def _round_up_mib(size):
@@ -683,27 +698,27 @@ class PartGetDiskFreeRegions(PartTestCas
         """Verify that it is possible to get info about free regions on a disk"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 1, 10 * 1024**2, BlockDev.PartAlign.NONE)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 1, 10 * 1024**2, BlockDev.PartAlign.NONE)
 
         # we should get proper data back
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        fis = BlockDev.part_get_disk_free_regions (self.loop_dev)
+        fis = BlockDev.part_get_disk_free_regions (self.loop_devs[0])
         self.assertEqual(len(fis), 1)
         fi = fis[0]
         self.assertEqual(fi.start, _round_up_mib(ps.start + ps.size))
         self.assertGreaterEqual(fi.size, 89 * 1024**2)
 
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 10 * 1024**2,
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 10 * 1024**2,
                                         10 * 1024**2, BlockDev.PartAlign.NONE)
-        fis = BlockDev.part_get_disk_free_regions (self.loop_dev)
+        fis = BlockDev.part_get_disk_free_regions (self.loop_devs[0])
         self.assertEqual(len(fis), 2)  # first part, gap, second part, free
         fi = fis[0]
         self.assertEqual(fi.start, _round_up_mib(512 + 10 * 1024**2))
@@ -712,11 +727,11 @@ class PartGetDiskFreeRegions(PartTestCas
         self.assertEqual(fi.start, _round_up_mib(512 + 30 * 1024**2))
         self.assertGreaterEqual(fi.size, 69 * 1024**2)
 
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps.start + ps.size + 1,
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps.start + ps.size + 1,
                                         50 * 1024**2, BlockDev.PartAlign.NONE)
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps.start + 1024**2,
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps.start + 1024**2,
                                         10 * 1024**2, BlockDev.PartAlign.NONE)
-        fis = BlockDev.part_get_disk_free_regions (self.loop_dev)
+        fis = BlockDev.part_get_disk_free_regions (self.loop_devs[0])
         self.assertEqual(len(fis), 3)  # first part, gap[0], second part, extended, logical, free extended[1], free[2]
 
         fi = fis[0]
@@ -730,19 +745,19 @@ class PartGetDiskFreeRegions(PartTestCas
         self.assertGreaterEqual(fi.size, 19 * 1024**2)
 
         # now something simple with GPT
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 1, 10 * 1024**2, BlockDev.PartAlign.NONE)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 1, 10 * 1024**2, BlockDev.PartAlign.NONE)
 
         # we should get proper data back
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        fis = BlockDev.part_get_disk_free_regions (self.loop_dev)
+        fis = BlockDev.part_get_disk_free_regions (self.loop_devs[0])
         self.assertEqual(len(fis), 1)
         fi = fis[0]
         self.assertEqual(fi.start, _round_up_mib(ps.start + ps.size))
@@ -753,72 +768,72 @@ class PartGetBestFreeRegion(PartTestCase
         """Verify that it is possible to get info about the best free region on a disk"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
-        ps1 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 1, 10 * 1024**2, BlockDev.PartAlign.NONE)
+        ps1 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 1, 10 * 1024**2, BlockDev.PartAlign.NONE)
         self.assertTrue(ps1)
-        self.assertEqual(ps1.path, self.loop_dev + "1")
+        self.assertEqual(ps1.path, self.loop_devs[0] + "1")
         self.assertEqual(ps1.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps1.start, 512)
         self.assertEqual(ps1.size, 10 * 1024**2)
 
         # create a 20MiB gap between the partitions
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps1.start + ps1.size + 20 * 1024**2,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps1.start + ps1.size + 20 * 1024**2,
                                          10 * 1024**2, BlockDev.PartAlign.NONE)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps2.start, ps1.start + ps1.size + 20 * 1024**2)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
         # normal partition should go in between the partitions because there's enough space for it
-        ps = BlockDev.part_get_best_free_region (self.loop_dev, BlockDev.PartType.NORMAL, 10 * 1024**2)
+        ps = BlockDev.part_get_best_free_region (self.loop_devs[0], BlockDev.PartType.NORMAL, 10 * 1024**2)
         self.assertLess(ps.start, ps2.start)
 
         # extended partition should be as big as possible so it shouldn't go in between the partitions
-        ps = BlockDev.part_get_best_free_region (self.loop_dev, BlockDev.PartType.EXTENDED, 10 * 1024**2)
+        ps = BlockDev.part_get_best_free_region (self.loop_devs[0], BlockDev.PartType.EXTENDED, 10 * 1024**2)
         self.assertGreaterEqual(ps.start, ps2.start + ps2.size)
 
         # create a 10MiB gap between the partitions
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps2.start + ps2.size + 10 * 1024**2,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps2.start + ps2.size + 10 * 1024**2,
                                          45 * 1024**2, BlockDev.PartAlign.NONE)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.EXTENDED)
         self.assertEqual(ps3.start, ps2.start + ps2.size + 10 * 1024**2)
         self.assertEqual(ps3.size, 45 * 1024**2)
 
         # there should now be 5 MiB left after the third partition which is enough for a 3MiB partition
-        ps = BlockDev.part_get_best_free_region (self.loop_dev, BlockDev.PartType.NORMAL, 3 * 1024**2)
+        ps = BlockDev.part_get_best_free_region (self.loop_devs[0], BlockDev.PartType.NORMAL, 3 * 1024**2)
         self.assertGreaterEqual(ps.start, ps3.start + ps3.size)
 
         # 7MiB partition should go in between the second and third partitions because there's enough space
         # for it there
-        ps = BlockDev.part_get_best_free_region (self.loop_dev, BlockDev.PartType.NORMAL, 7 * 1024**2)
+        ps = BlockDev.part_get_best_free_region (self.loop_devs[0], BlockDev.PartType.NORMAL, 7 * 1024**2)
         self.assertGreaterEqual(ps.start, ps2.start + ps2.size)
         self.assertLess(ps.start, ps3.start)
 
         # 15MiB partition should go in between the first and second partitions because that's the only
         # space big enough for it
-        ps = BlockDev.part_get_best_free_region (self.loop_dev, BlockDev.PartType.NORMAL, 15 * 1024**2)
+        ps = BlockDev.part_get_best_free_region (self.loop_devs[0], BlockDev.PartType.NORMAL, 15 * 1024**2)
         self.assertGreaterEqual(ps.start, ps1.start + ps1.size)
         self.assertLess(ps.start, ps2.start)
 
-        ps5 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps3.start + 20 * 1024**2,
+        ps5 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps3.start + 20 * 1024**2,
                                          15 * 1024**2, BlockDev.PartAlign.NONE)
-        self.assertEqual(ps5.path, self.loop_dev + "5")
+        self.assertEqual(ps5.path, self.loop_devs[0] + "5")
         self.assertEqual(ps5.type, BlockDev.PartType.LOGICAL)
         self.assertEqual(ps5.start, ps3.start + 20 * 1024**2)
         self.assertEqual(ps5.size, 15 * 1024**2)
 
         # 5MiB logical partition should go after the fifth partition because there's enough space for it
-        ps = BlockDev.part_get_best_free_region (self.loop_dev, BlockDev.PartType.LOGICAL, 5 * 1024**2)
+        ps = BlockDev.part_get_best_free_region (self.loop_devs[0], BlockDev.PartType.LOGICAL, 5 * 1024**2)
         self.assertGreaterEqual(ps.start, ps5.start + ps5.size)
         self.assertLess(ps.start, ps3.start + ps3.size)
 
         # 15MiB logical partition should go before the fifth partition because there's enough space for it
-        ps = BlockDev.part_get_best_free_region (self.loop_dev, BlockDev.PartType.LOGICAL, 15 * 1024**2)
+        ps = BlockDev.part_get_best_free_region (self.loop_devs[0], BlockDev.PartType.LOGICAL, 15 * 1024**2)
         self.assertGreaterEqual(ps.start, ps3.start)
         self.assertLess(ps.start, ps5.start)
 
@@ -829,32 +844,32 @@ class PartGetPartByPos(PartTestCase):
         ## prepare the disk with non-trivial setup first
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps)
-        self.assertEqual(ps.path, self.loop_dev + "1")
+        self.assertEqual(ps.path, self.loop_devs[0] + "1")
         self.assertEqual(ps.type, BlockDev.PartType.NORMAL)
         self.assertEqual(ps.start, 2048 * 512)
         self.assertEqual(ps.size, 10 * 1024**2)
 
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps.start + ps.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps2)
-        self.assertEqual(ps2.path, self.loop_dev + "2")
+        self.assertEqual(ps2.path, self.loop_devs[0] + "2")
         self.assertEqual(ps2.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
         self.assertLess(abs(ps2.start - (ps.start + ps.size + 1)), ps.start)
         self.assertEqual(ps2.size, 10 * 1024**2)
 
-        ps3 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.EXTENDED, ps2.start + ps2.size + 1,
+        ps3 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.EXTENDED, ps2.start + ps2.size + 1,
                                          35 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps3)
-        self.assertEqual(ps3.path, self.loop_dev + "3")
+        self.assertEqual(ps3.path, self.loop_devs[0] + "3")
         self.assertEqual(ps3.type, BlockDev.PartType.EXTENDED)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -863,30 +878,30 @@ class PartGetPartByPos(PartTestCase):
 
         # the logical partition has number 5 even though the extended partition
         # has number 3
-        ps5 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps3.start + 1,
+        ps5 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps3.start + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps5)
-        self.assertEqual(ps5.path, self.loop_dev + "5")
+        self.assertEqual(ps5.path, self.loop_devs[0] + "5")
         self.assertEqual(ps5.type, BlockDev.PartType.LOGICAL)
         # the start has to be somewhere in the extended partition p3 which
         # should need at most 2 MiB extra space
         self.assertTrue(ps3.start < ps5.start < ps3.start + ps3.size)
         self.assertLess(abs(ps5.size - 10 * 1024**2), 2 * 1024**2)
 
-        ps6 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps5.start + ps5.size + 1,
+        ps6 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps5.start + ps5.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps6)
-        self.assertEqual(ps6.path, self.loop_dev + "6")
+        self.assertEqual(ps6.path, self.loop_devs[0] + "6")
         self.assertEqual(ps6.type, BlockDev.PartType.LOGICAL)
         # the start has to be somewhere in the extended partition p3 which
         # should need at most 2 MiB extra space
         self.assertTrue(ps3.start < ps6.start < ps3.start + ps3.size)
         self.assertEqual(ps6.size, 10 * 1024**2)
 
-        ps7 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.LOGICAL, ps6.start + ps6.size + 2 * 1024**2,
+        ps7 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.LOGICAL, ps6.start + ps6.size + 2 * 1024**2,
                                          5 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps7)
-        self.assertEqual(ps7.path, self.loop_dev + "7")
+        self.assertEqual(ps7.path, self.loop_devs[0] + "7")
         self.assertEqual(ps7.type, BlockDev.PartType.LOGICAL)
         # the start has to be somewhere in the extended partition p3 which
         # should need at most 2 MiB extra space
@@ -895,10 +910,10 @@ class PartGetPartByPos(PartTestCase):
         self.assertEqual(ps7.size, 5 * 1024**2)
 
         # here we go with the partition number 4
-        ps4 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps3.start + ps3.size + 1,
+        ps4 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps3.start + ps3.size + 1,
                                          10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(ps4)
-        self.assertEqual(ps4.path, self.loop_dev + "4")
+        self.assertEqual(ps4.path, self.loop_devs[0] + "4")
         self.assertEqual(ps4.type, BlockDev.PartType.NORMAL)
         # the start has to be at most as far from the end of the previous part
         # as is the start of the first part from the start of the disk
@@ -909,7 +924,7 @@ class PartGetPartByPos(PartTestCase):
         ## now try to get the partitions
         # XXX: Any way to get the extended partition (ps3)? Let's just skip it now.
         for part in (ps, ps2, ps5, ps6, ps7, ps4):
-            ret = BlockDev.part_get_part_by_pos(self.loop_dev, part.start + 1 * 1024**2)
+            ret = BlockDev.part_get_part_by_pos(self.loop_devs[0], part.start + 1 * 1024**2)
             self.assertIsNotNone(ret)
             self.assertEqual(ret.path, part.path)
             self.assertEqual(ret.start, part.start)
@@ -917,7 +932,7 @@ class PartGetPartByPos(PartTestCase):
             self.assertEqual(ret.type, part.type)
 
         # free space in the extended partition
-        ret = BlockDev.part_get_part_by_pos(self.loop_dev, ps3.start + 33 * 1024**2)
+        ret = BlockDev.part_get_part_by_pos(self.loop_devs[0], ps3.start + 33 * 1024**2)
         self.assertIsNotNone(ret)
         self.assertIsNone(ret.path)
         self.assertTrue(ret.type & BlockDev.PartType.FREESPACE)
@@ -928,7 +943,7 @@ class PartGetPartByPos(PartTestCase):
         self.assertLess(ret.size, 10 * 1024**2)
 
         # free space at the end of the disk
-        ret = BlockDev.part_get_part_by_pos(self.loop_dev, 90 * 1024**2)
+        ret = BlockDev.part_get_part_by_pos(self.loop_devs[0], 90 * 1024**2)
         self.assertIsNotNone(ret)
         self.assertIsNone(ret.path)
         self.assertTrue(ret.type & BlockDev.PartType.FREESPACE)
@@ -936,7 +951,7 @@ class PartGetPartByPos(PartTestCase):
         self.assertLessEqual(ret.size, (100 * 1024**2) - (ps4.start + ps4.size))
 
         # metadata at the start of the extended partition
-        ret = BlockDev.part_get_part_by_pos(self.loop_dev, ps3.start)
+        ret = BlockDev.part_get_part_by_pos(self.loop_devs[0], ps3.start)
         self.assertIsNotNone(ret)
         self.assertIsNone(ret.path)
         self.assertTrue(ret.type & BlockDev.PartType.LOGICAL)
@@ -946,7 +961,7 @@ class PartGetPartByPos(PartTestCase):
 
         # metadata after a logical partition
         for ps in (ps5, ps6, ps7):
-            ret = BlockDev.part_get_part_by_pos(self.loop_dev, ps.start + ps.size)
+            ret = BlockDev.part_get_part_by_pos(self.loop_devs[0], ps.start + ps.size)
             self.assertIsNotNone(ret)
             self.assertIsNone(ret.path)
             self.assertTrue(ret.type & BlockDev.PartType.LOGICAL)
@@ -959,42 +974,42 @@ class PartCreateResizePartCase(PartTestC
         """Verify that it is possible to create and resize two partitions"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         ps1_half = 20 * 1024**2
         ps1_start = 2 * 1024**2
 
         # create a maximal second partition
-        ps2 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2* ps1_half, 0, BlockDev.PartAlign.NONE)
+        ps2 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2* ps1_half, 0, BlockDev.PartAlign.NONE)
         self.assertGreaterEqual(ps2.start, 2* ps1_half)
 
         # create one maximal partition in the beginning
-        ps1 = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, ps1_start, 0, BlockDev.PartAlign.NONE)
+        ps1 = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, ps1_start, 0, BlockDev.PartAlign.NONE)
         self.assertGreaterEqual(ps1.size, ps1_half)
         self.assertGreaterEqual(ps1.start, ps1_start)
         self.assertLess(ps1.size, ps1_half * 2)  # can't have full size from beginning to ps2 because of start offset
 
         # resizing should give the same result
         ps1_size = ps1.size
-        succ = BlockDev.part_resize_part (self.loop_dev, ps1.path, 0, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps1.path, 0, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps1 = BlockDev.part_get_part_spec(self.loop_dev, ps1.path)
+        ps1 = BlockDev.part_get_part_spec(self.loop_devs[0], ps1.path)
         self.assertEqual(ps1.start, ps1_start)  # offset must not be moved
         self.assertEqual(ps1.size, ps1_size)
 
-        succ = BlockDev.part_resize_part (self.loop_dev, ps1.path, ps1_half, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps1.path, ps1_half, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(succ)
-        ps1 = BlockDev.part_get_part_spec(self.loop_dev, ps1.path)
+        ps1 = BlockDev.part_get_part_spec(self.loop_devs[0], ps1.path)
         self.assertEqual(ps1.start, ps1_start)  # offset must not be moved
         self.assertGreaterEqual(ps1.size, ps1_half)  # at least requested size
         self.assertLess(ps1.size, ps1_half + 2048 * self.block_size)  # but also not too big (assuming end alignment based on sector size)
 
         ps2_size = ps2.size
         ps2_start = ps2.start
-        succ = BlockDev.part_resize_part (self.loop_dev, ps2.path, 0, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps2.path, 0, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(succ)
-        ps2 = BlockDev.part_get_part_spec(self.loop_dev, ps2.path)
+        ps2 = BlockDev.part_get_part_spec(self.loop_devs[0], ps2.path)
         self.assertEqual(ps2.start, ps2_start)  # offset must not be moved
         self.assertGreaterEqual(ps2.size, ps2_size - 2 * 1024**2)  # almost as big as before
 
@@ -1003,7 +1018,7 @@ class PartCreateResizePartCase(PartTestC
 
         try:
             fdisk_version = self._get_fdisk_version()
-        except Exception as e:
+        except Exception:
             resize_tolerance = 0
             fdisk_version = Version("0")
 
@@ -1017,57 +1032,57 @@ class PartCreateResizePartCase(PartTestC
             resize_tolerance = 0
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # create a maximal partition
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2 * 1024**2, 0, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2 * 1024**2, 0, BlockDev.PartAlign.OPTIMAL)
         initial_start = ps.start
         initial_size = ps.size
 
         new_size = 20 * 1000**2  # resize to MB (not MiB) for a non-multiple of the blocksize
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, new_size, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, new_size, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)  # offset must not be moved
         self.assertGreaterEqual(ps.size, new_size)  # at least the requested size
         self.assertLess(ps.size, new_size + 2048 * self.block_size)  # but also not too big (assuming end alignment based on sector size)
 
         # resize to maximum
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, 0, BlockDev.PartAlign.OPTIMAL)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, 0, BlockDev.PartAlign.OPTIMAL)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)
         self.assertEqual(initial_size, ps.size)  # should grow to the same size again
 
         # resize to maximum explicitly with no alignment (we know exact size)
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, initial_size, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, initial_size, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)
         self.assertGreaterEqual(ps.size, initial_size) # at least the requested size
 
         # resize back to 20 MB (not MiB) with no alignment
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, new_size, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, new_size, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)  # offset must not be moved
         self.assertGreaterEqual(ps.size, new_size)  # at least the requested size
         self.assertLess(ps.size, new_size + 4 * 1024)  # but also not too big (assuming max. 4 KiB blocks)
 
         # resize to maximum with no alignment
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, 0, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, 0, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)
         self.assertGreaterEqual(ps.size, initial_size - resize_tolerance)
         new_size = ps.size
         unaligned_max = new_size
 
         # resize to maximum with no alignment explicitly
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, new_size, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, new_size, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)
         self.assertGreaterEqual(ps.size, new_size) # at least the requested size
 
@@ -1076,28 +1091,28 @@ class PartCreateResizePartCase(PartTestC
             return
 
         # resize to previous maximum with no alignment explicitly
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, initial_size, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, initial_size, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)
         self.assertGreaterEqual(ps.size, initial_size - resize_tolerance)
 
         # resize back to 20 MB (not MiB) with no alignment
         new_size = 20 * 1000**2
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, new_size, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, new_size, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)  # offset must not be moved
         self.assertGreaterEqual(ps.size, new_size)  # at least the requested size
         self.assertLess(ps.size, new_size + 4 * 1024)  # but also not too big (assuming max. 4 KiB blocks)
 
         # resize should allow up to 4 MiB over unaligned max size
         with self.assertRaisesRegex(GLib.GError, "is bigger than max size"):
-            BlockDev.part_resize_part (self.loop_dev, ps.path, unaligned_max + 4 * 1024**2 + 1, BlockDev.PartAlign.NONE)
+            BlockDev.part_resize_part (self.loop_devs[0], ps.path, unaligned_max + 4 * 1024**2 + 1, BlockDev.PartAlign.NONE)
 
-        succ = BlockDev.part_resize_part (self.loop_dev, ps.path, unaligned_max + 4 * 1024**2, BlockDev.PartAlign.NONE)
+        succ = BlockDev.part_resize_part (self.loop_devs[0], ps.path, unaligned_max + 4 * 1024**2, BlockDev.PartAlign.NONE)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec(self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec(self.loop_devs[0], ps.path)
         self.assertEqual(initial_start, ps.start)
         self.assertGreaterEqual(ps.size, unaligned_max) # at least the requested size
 
@@ -1112,18 +1127,18 @@ class PartCreateDeletePartCase(PartTestC
         """Verify that it is possible to create and delete a partition"""
 
         # we first need a partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
-        pss = BlockDev.part_get_disk_parts (self.loop_dev)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        pss = BlockDev.part_get_disk_parts (self.loop_devs[0])
         self.assertEqual(len(pss), 1)
 
-        succ = BlockDev.part_delete_part (self.loop_dev, ps.path)
+        succ = BlockDev.part_delete_part (self.loop_devs[0], ps.path)
         self.assertTrue(succ)
-        pss = BlockDev.part_get_disk_parts (self.loop_dev)
+        pss = BlockDev.part_get_disk_parts (self.loop_devs[0])
         self.assertEqual(len(pss), 0)
 
 
@@ -1132,41 +1147,41 @@ class PartSetNameCase(PartTestCase):
         """Verify that it is possible to set partition name"""
 
         # we first need a GPT partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should get proper data back
         self.assertTrue(ps)
         self.assertIn(ps.name, ("", None))  # no name
 
-        succ = BlockDev.part_set_part_name (self.loop_dev, ps.path, "TEST")
+        succ = BlockDev.part_set_part_name (self.loop_devs[0], ps.path, "TEST")
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.name, "TEST")
 
-        succ = BlockDev.part_set_part_name (self.loop_dev, ps.path, "")
+        succ = BlockDev.part_set_part_name (self.loop_devs[0], ps.path, "")
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.name, "")
 
         # let's now test an MSDOS partition table (doesn't support names)
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should get proper data back
         self.assertTrue(ps)
         self.assertIn(ps.name, ("", None))  # no name
 
         with self.assertRaises(GLib.GError):
-            BlockDev.part_set_part_name (self.loop_dev, ps.path, "")
+            BlockDev.part_set_part_name (self.loop_devs[0], ps.path, "")
 
         # we should still get proper data back though
         self.assertTrue(ps)
@@ -1181,32 +1196,32 @@ class PartSetUUIDCase(PartTestCase):
         """Verify that it is possible to set partition UUID"""
 
         # we first need a GPT partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
-        succ = BlockDev.part_set_part_uuid (self.loop_dev, ps.path, self.test_uuid)
+        succ = BlockDev.part_set_part_uuid (self.loop_devs[0], ps.path, self.test_uuid)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.uuid, self.test_uuid)
 
         # let's now test an MSDOS partition table (doesn't support UUIDs)
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should still get proper data back
         self.assertTrue(ps)
         self.assertIn(ps.uuid, ("", None))  # no name
 
         with self.assertRaises(GLib.GError):
-            BlockDev.part_set_part_name (self.loop_dev, ps.path, self.test_uuid)
+            BlockDev.part_set_part_name (self.loop_devs[0], ps.path, self.test_uuid)
 
 
 class PartSetTypeCase(PartTestCase):
@@ -1214,43 +1229,43 @@ class PartSetTypeCase(PartTestCase):
         """Verify that it is possible to set and get partition type"""
 
         # we first need a GPT partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should get proper data back
         self.assertTrue(ps)
         self.assertTrue(ps.type_guid)  # should have some type
 
-        succ = BlockDev.part_set_part_type (self.loop_dev, ps.path, "E6D6D379-F507-44C2-A23C-238F2A3DF928")
+        succ = BlockDev.part_set_part_type (self.loop_devs[0], ps.path, "E6D6D379-F507-44C2-A23C-238F2A3DF928")
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.type_guid, "E6D6D379-F507-44C2-A23C-238F2A3DF928")
         self.assertEqual(ps.type_name, "Linux LVM")
 
-        succ = BlockDev.part_set_part_type (self.loop_dev, ps.path, "0FC63DAF-8483-4772-8E79-3D69D8477DE4")
+        succ = BlockDev.part_set_part_type (self.loop_devs[0], ps.path, "0FC63DAF-8483-4772-8E79-3D69D8477DE4")
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.type_guid, "0FC63DAF-8483-4772-8E79-3D69D8477DE4")
         self.assertEqual(ps.type_name, "Linux filesystem")
 
         # let's now test an MSDOS partition table (doesn't support type GUIDs)
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should get proper data back
         self.assertTrue(ps)
         self.assertIn(ps.type_guid, ("", None))  # no type GUID
 
         with self.assertRaises(GLib.GError):
-            BlockDev.part_set_part_type (self.loop_dev, ps.path, "0FC63DAF-8483-4772-8E79-3D69D8477DE4")
+            BlockDev.part_set_part_type (self.loop_devs[0], ps.path, "0FC63DAF-8483-4772-8E79-3D69D8477DE4")
 
         # we should still get proper data back though
         self.assertTrue(ps)
@@ -1261,25 +1276,25 @@ class PartSetIdCase(PartTestCase):
         """Verify that it is possible to set partition id (msdos partition type)"""
 
         # we first need an MBR partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # we should get proper data back
         self.assertTrue(ps)
 
-        succ = BlockDev.part_set_part_id (self.loop_dev, ps.path, "0x8e")
+        succ = BlockDev.part_set_part_id (self.loop_devs[0], ps.path, "0x8e")
         self.assertTrue(succ)
 
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.id, "0x8e")
 
         # we can't change part id to extended partition id
         with self.assertRaises(GLib.GError):
-            BlockDev.part_set_part_id (self.loop_dev, ps.path, "0x85")
+            BlockDev.part_set_part_id (self.loop_devs[0], ps.path, "0x85")
 
 
 class PartSetBootableFlagCase(PartTestCase):
@@ -1287,23 +1302,23 @@ class PartSetBootableFlagCase(PartTestCa
         """Verify that it is possible to set bootable flag on MSDOS"""
 
         # we first need a MBR partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.MSDOS, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.MSDOS, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # set the flag
-        succ = BlockDev.part_set_part_bootable (self.loop_dev, ps.path, True)
+        succ = BlockDev.part_set_part_bootable (self.loop_devs[0], ps.path, True)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertTrue(ps.bootable)
 
         # unset the flag
-        succ = BlockDev.part_set_part_bootable (self.loop_dev, ps.path, False)
+        succ = BlockDev.part_set_part_bootable (self.loop_devs[0], ps.path, False)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertFalse(ps.bootable)
 
 
@@ -1314,17 +1329,17 @@ class PartSetGptFlagsCase(PartTestCase):
         esp_guid = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
 
         # we first need a GPT partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # set GUID (part type)
-        succ = BlockDev.part_set_part_type (self.loop_dev, ps.path, esp_guid)
+        succ = BlockDev.part_set_part_type (self.loop_devs[0], ps.path, esp_guid)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.type_guid, esp_guid)
         self.assertEqual(ps.type_name, "EFI System")
 
@@ -1334,34 +1349,19 @@ class PartSetGptAttrsCase(PartTestCase):
         """Verify that it is possible to set and get partition attributes"""
 
         # we first need a GPT partition table
-        succ = BlockDev.part_create_table (self.loop_dev, BlockDev.PartTableType.GPT, True)
+        succ = BlockDev.part_create_table (self.loop_devs[0], BlockDev.PartTableType.GPT, True)
         self.assertTrue(succ)
 
         # for now, let's just create a typical primary partition starting at the
         # sector 2048, 10 MiB big with optimal alignment
-        ps = BlockDev.part_create_part (self.loop_dev, BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
+        ps = BlockDev.part_create_part (self.loop_devs[0], BlockDev.PartTypeReq.NORMAL, 2048*512, 10 * 1024**2, BlockDev.PartAlign.OPTIMAL)
 
         # set some GPT attributes
         attrs = 0
         attrs |= (1 << 0)  # system partition
         attrs |= (1 << 60)  # read only
         attrs |= (1 << 62)  # hidden
-        succ = BlockDev.part_set_part_attributes (self.loop_dev, ps.path, attrs)
+        succ = BlockDev.part_set_part_attributes (self.loop_devs[0], ps.path, attrs)
         self.assertTrue(succ)
-        ps = BlockDev.part_get_part_spec (self.loop_dev, ps.path)
+        ps = BlockDev.part_get_part_spec (self.loop_devs[0], ps.path)
         self.assertEqual(ps.attrs, attrs)
-
-
-class PartNoDevCase(PartTestCase):
-
-    def setUp(self):
-        # no devices needed for this test case
-        pass
-
-    def test_part_type_str(self):
-        types = {BlockDev.PartType.NORMAL: 'primary', BlockDev.PartType.LOGICAL: 'logical',
-                 BlockDev.PartType.EXTENDED: 'extended', BlockDev.PartType.FREESPACE: 'free',
-                 BlockDev.PartType.METADATA: 'metadata', BlockDev.PartType.PROTECTED: 'primary'}
-
-        for key, value in types.items():
-            self.assertEqual(BlockDev.part_get_type_str(key), value)
diff -pruN 3.3.1-3/tests/run_tests.py 3.4.0-1/tests/run_tests.py
--- 3.3.1-3/tests/run_tests.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/run_tests.py	2025-09-24 13:46:42.000000000 +0000
@@ -17,7 +17,7 @@ import yaml
 
 from utils import TestTags, get_version
 
-LIBDIRS = 'src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/lib/.libs:src/plugins/nvme/.libs:src/plugins/smart/.libs'
+LIBDIRS = 'src/utils/.libs:src/plugins/.libs:src/plugins/fs/.libs:src/lib/.libs:src/plugins/lvm/.libs:src/plugins/nvme/.libs:src/plugins/smart/.libs'
 GIDIR = 'src/lib'
 
 SKIP_CONFIG = 'skip.yml'
@@ -194,7 +194,7 @@ def parse_args():
 def _split_test_id(test_id):
     # test.id() looks like 'crypto_test.CryptoTestResize.test_luks2_resize'
     # and we want to print 'test_luks2_resize (crypto_test.CryptoTestResize)'
-    test_desc = test.id().split(".")
+    test_desc = test_id.split(".")
     test_name = test_desc[-1]
     test_module = ".".join(test_desc[:-1])
 
@@ -342,9 +342,13 @@ if __name__ == '__main__':
         if skip_id:
             test_name, test_module = _split_test_id(test_id)
             reason = "not supported on this distribution in this version and arch: %s" % skipping[skip_id]
-            print("%s (%s)\n%s ... skipped '%s'" % (test_name, test_module,
-                                                    test._testMethodDoc, reason),
-                  file=sys.stderr)
+            if test._testMethodDoc:
+                print("%s (%s)\n%s ... skipped '%s'" % (test_name, test_module,
+                                                        test._testMethodDoc, reason),
+                      file=sys.stderr)
+            else:
+                print("%s (%s) ... skipped '%s'" % (test_name, test_module, reason),
+                      file=sys.stderr)
             continue
 
         # finally add the test to the suite
diff -pruN 3.3.1-3/tests/s390_test.py 3.4.0-1/tests/s390_test.py
--- 3.3.1-3/tests/s390_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/s390_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -25,7 +25,7 @@ class S390TestCase(unittest.TestCase):
 
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.S390), "libbd_s390.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.S390), "libbd_s390.so.3")
 
     @tag_test(TestTags.EXTRADEPS, TestTags.NOSTORAGE)
     def test_device_input(self):
diff -pruN 3.3.1-3/tests/skip.yml 3.4.0-1/tests/skip.yml
--- 3.3.1-3/tests/skip.yml	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/skip.yml	2025-09-24 13:46:42.000000000 +0000
@@ -39,18 +39,12 @@
     - distro: "debian"
       reason: "mount.ntfs-3g randomly hangs on Debian testing"
 
-- test: lvm_dbus_tests.LvmTestLVsnapshots.test_snapshotcreate_lvorigin_snapshotmerge
-  skip_on:
-    - distro: "centos"
-      version: "9"
-      reason: "snapshot merge doesn't work on CentOS 9 Stream with LVM DBus API"
-
-- test: (lvm_test|lvm_dbus_tests).LvmPVVGLVWritecacheAttachDetachTestCase
+- test: (lvm_test|lvm_dbus_tests).LvmTestCache.test_writecache_attach_detach
   skip_on:
     - arch: "i686"
       reason: "Cache attach/detach fails with ENOMEM on 32bit systems"
 
-- test: (lvm_test|lvm_dbus_tests).LVMVDOTest
+- test: (lvm_test|lvm_dbus_tests).LvmVDOTest
   skip_on:
     - distro: "debian"
       arch: "i686"
@@ -65,3 +59,20 @@
     - distro: "debian"
       version: "12"
       reason: "LVM >= 2.03.17 needed for LVM config parsing with --valuesonly"
+
+- test: nvdimm_test
+  skip_on:
+    - distro: ["fedora", "centos", "debian"]
+      reason: "NVDIMM plugin is deprecated"
+
+- test: crypto_test.CryptoTestOpenClose.test_luks_open_close
+  skip_on:
+    - distro: "fedora"
+      version: "43"
+      reason: "Bug in cryptsetup aborting when activating LUKSv1 with keyring"
+
+- test: mdraid_test.MDTestNominateDenominate.test_nominate_denominate
+  skip_on:
+    - distro: "centos"
+      version: ["9", "10"]
+      reason: "Race condition in denominate with latest mdadm v4.4"
diff -pruN 3.3.1-3/tests/smart_test.py 3.4.0-1/tests/smart_test.py
--- 3.3.1-3/tests/smart_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/smart_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -1,12 +1,8 @@
 import unittest
 import os
-import re
-import glob
-import time
-import shutil
 import overrides_hack
 
-from utils import run, create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, TestTags, tag_test, write_file, run_command, required_plugins
+from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, TestTags, tag_test, required_plugins, setup_scsi_debug, clean_scsi_debug
 
 import gi
 gi.require_version('GLib', '2.0')
@@ -61,32 +57,9 @@ class SMARTTest(unittest.TestCase):
             pass
         os.unlink(self.loop_dev_file)
 
-    def _setup_scsi_debug(self):
-        res, _out, _err = run_command('modprobe scsi_debug')
-        self.assertEqual(res, 0)
-        dirs = []
-        while len(dirs) < 1:
-            dirs = glob.glob('/sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block')
-            time.sleep(0.1)
-        self.scsi_debug_dev = os.listdir(dirs[0])
-        self.assertEqual(len(self.scsi_debug_dev), 1)
-        self.scsi_debug_dev = '/dev/' + self.scsi_debug_dev[0]
-        self.assertTrue(os.path.exists(self.scsi_debug_dev))
-
-    def _clean_scsi_debug(self):
-        try:
-            device = self.scsi_debug_dev.split('/')[-1]
-            if os.path.exists('/sys/block/' + device):
-                self.write_file('/sys/block/%s/device/delete' % device, '1')
-            while os.path.exists(device):
-                time.sleep(0.1)
-            self.run_command('modprobe -r scsi_debug')
-        except:
-            pass
-
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SMART), "libbd_smart.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SMART), "libbd_smart.so.3")
 
     @tag_test(TestTags.CORE)
     def test_ata_info(self):
@@ -112,8 +85,8 @@ class SMARTTest(unittest.TestCase):
             BlockDev.smart_ata_get_info(self.loop_dev)
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         with self.assertRaisesRegex(GLib.GError, msg):
             BlockDev.smart_ata_get_info(self.scsi_debug_dev)
 
@@ -146,8 +119,8 @@ class SMARTTest(unittest.TestCase):
             BlockDev.smart_set_enabled(self.loop_dev, True)
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         with self.assertRaisesRegex(GLib.GError, msg):
             BlockDev.smart_set_enabled(self.scsi_debug_dev, False)
         with self.assertRaisesRegex(GLib.GError, msg):
@@ -184,8 +157,8 @@ class SMARTTest(unittest.TestCase):
                 BlockDev.smart_device_self_test(self.loop_dev, t)
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         for t in tests:
             with self.assertRaisesRegex(GLib.GError, msg):
                 BlockDev.smart_device_self_test(self.scsi_debug_dev, t)
@@ -201,6 +174,7 @@ class SMARTTest(unittest.TestCase):
 
         # LIO device (SCSI)
         self._setup_lio()
+        self.addCleanup(self._clean_lio)
         with self.assertRaisesRegex(GLib.GError, msg):
             BlockDev.smart_scsi_get_info(self.lio_dev)
 
@@ -211,8 +185,8 @@ class SMARTTest(unittest.TestCase):
             BlockDev.smart_scsi_get_info(self.loop_dev)
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         with self.assertRaisesRegex(GLib.GError, msg):
             BlockDev.smart_scsi_get_info(self.scsi_debug_dev)
 
diff -pruN 3.3.1-3/tests/smartmontools_test.py 3.4.0-1/tests/smartmontools_test.py
--- 3.3.1-3/tests/smartmontools_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/smartmontools_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -1,12 +1,9 @@
 import unittest
 import os
-import re
-import glob
-import time
 import shutil
 import overrides_hack
 
-from utils import run, create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, fake_path, TestTags, tag_test, write_file, run_command, required_plugins
+from utils import create_sparse_tempfile, create_lio_device, delete_lio_device, fake_utils, TestTags, tag_test, required_plugins, setup_scsi_debug, clean_scsi_debug
 
 import gi
 gi.require_version('GLib', '2.0')
@@ -60,32 +57,9 @@ class SmartmontoolsTest(unittest.TestCas
             pass
         os.unlink(self.loop_dev_file)
 
-    def _setup_scsi_debug(self):
-        res, _out, _err = run_command('modprobe scsi_debug')
-        self.assertEqual(res, 0)
-        dirs = []
-        while len(dirs) < 1:
-            dirs = glob.glob('/sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block')
-            time.sleep(0.1)
-        self.scsi_debug_dev = os.listdir(dirs[0])
-        self.assertEqual(len(self.scsi_debug_dev), 1)
-        self.scsi_debug_dev = '/dev/' + self.scsi_debug_dev[0]
-        self.assertTrue(os.path.exists(self.scsi_debug_dev))
-
-    def _clean_scsi_debug(self):
-        try:
-            device = self.scsi_debug_dev.split('/')[-1]
-            if os.path.exists('/sys/block/' + device):
-                self.write_file('/sys/block/%s/device/delete' % device, '1')
-            while os.path.exists(device):
-                time.sleep(0.1)
-            self.run_command('modprobe -r scsi_debug')
-        except:
-            pass
-
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SMART), "libbd_smartmontools.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SMART), "libbd_smartmontools.so.3")
 
     @tag_test(TestTags.CORE)
     def test_ata_info(self):
@@ -133,8 +107,8 @@ class SmartmontoolsTest(unittest.TestCas
             BlockDev.smart_ata_get_info(self.loop_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         msg = r"Error parsing smartctl JSON data: The member .ata_smart_data. is not defined in the object at the current position."
         with self.assertRaisesRegex(GLib.GError, msg):
             BlockDev.smart_ata_get_info(self.scsi_debug_dev, None)
@@ -281,8 +255,8 @@ class SmartmontoolsTest(unittest.TestCas
             BlockDev.smart_set_enabled(self.loop_dev, True, [BlockDev.ExtraArg.new("--device=ata", "")])
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         BlockDev.smart_set_enabled(self.scsi_debug_dev, False)
         BlockDev.smart_set_enabled(self.scsi_debug_dev, True)
         BlockDev.smart_set_enabled(self.scsi_debug_dev, False, [BlockDev.ExtraArg.new("--device=scsi", "")])
@@ -345,8 +319,8 @@ class SmartmontoolsTest(unittest.TestCas
                 BlockDev.smart_device_self_test(self.loop_dev, t, [BlockDev.ExtraArg.new("--device=scsi", "")])
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         for t in [BlockDev.SmartSelfTestOp.OFFLINE, BlockDev.SmartSelfTestOp.SHORT,
                   BlockDev.SmartSelfTestOp.LONG, BlockDev.SmartSelfTestOp.CONVEYANCE,
                   BlockDev.SmartSelfTestOp.ABORT]:
@@ -375,6 +349,7 @@ class SmartmontoolsTest(unittest.TestCas
 
         # LIO device (SCSI)
         self._setup_lio()
+        self.addCleanup(self._clean_lio)
         msg = r"Error getting SCSI SMART info: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure."
         with self.assertRaisesRegex(GLib.GError, msg):
             BlockDev.smart_scsi_get_info(self.lio_dev, None)
@@ -397,8 +372,8 @@ class SmartmontoolsTest(unittest.TestCas
             BlockDev.smart_scsi_get_info(self.loop_dev, [BlockDev.ExtraArg.new("--device=ata", "")])
 
         # scsi_debug
-        self._setup_scsi_debug()
-        self.addCleanup(self._clean_scsi_debug)
+        self.scsi_debug_dev = setup_scsi_debug()
+        self.addCleanup(clean_scsi_debug, self.scsi_debug_dev)
         msg = r"Error getting SCSI SMART info: Some SMART or other ATA command to the disk failed, or there was a checksum error in a SMART data structure."
         with self.assertRaisesRegex(GLib.GError, msg):
             BlockDev.smart_scsi_get_info(self.scsi_debug_dev, None)
diff -pruN 3.3.1-3/tests/swap_test.py 3.4.0-1/tests/swap_test.py
--- 3.3.1-3/tests/swap_test.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/swap_test.py	2025-09-24 13:46:42.000000000 +0000
@@ -26,7 +26,7 @@ class SwapTest(unittest.TestCase):
 class SwapPluginVersionTestCase(SwapTest):
     @tag_test(TestTags.NOSTORAGE)
     def test_plugin_version(self):
-       self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SWAP), "libbd_swap.so.3")
+        self.assertEqual(BlockDev.get_plugin_soname(BlockDev.Plugin.SWAP), "libbd_swap.so.3")
 
 class SwapTestCase(SwapTest):
     test_uuid = "4d7086c4-a4d3-432f-819e-73da03870df9"
@@ -115,21 +115,14 @@ class SwapTestCase(SwapTest):
         on = BlockDev.swap_swapstatus(self.loop_dev)
         self.assertFalse(on)
 
-    def test_mkswap_with_label(self):
+    def test_mkswap_with_label_uuid(self):
         """Verify that mkswap with label works as expected"""
 
-        succ = BlockDev.swap_mkswap(self.loop_dev, "BlockDevSwap", None)
+        succ = BlockDev.swap_mkswap(self.loop_dev, label="BlockDevSwap", uuid=self.test_uuid)
         self.assertTrue(succ)
 
         _ret, out, _err = run_command("blkid -ovalue -sLABEL -p %s" % self.loop_dev)
         self.assertEqual(out, "BlockDevSwap")
-
-    def test_mkswap_with_uuid(self):
-        """Verify that mkswap with uuid works as expected"""
-
-        succ = BlockDev.swap_mkswap(self.loop_dev, uuid=self.test_uuid)
-        self.assertTrue(succ)
-
         _ret, out, _err = run_command("blkid -ovalue -sUUID -p %s" % self.loop_dev)
         self.assertEqual(out, self.test_uuid)
 
diff -pruN 3.3.1-3/tests/utils.py 3.4.0-1/tests/utils.py
--- 3.3.1-3/tests/utils.py	2025-06-18 07:12:41.000000000 +0000
+++ 3.4.0-1/tests/utils.py	2025-09-24 13:46:42.000000000 +0000
@@ -81,7 +81,7 @@ def fake_utils(path="."):
 ALL_UTILS = {"lvm", "btrfs", "mkswap", "swaplabel", "multipath", "mpathconf", "dmsetup", "mdadm",
              "mkfs.exfat", "fsck.exfat", "tune.exfat",
              "mke2fs", "e2fsck", "tune2fs", "dumpe2fs", "resize2fs",
-             "mkfs.f2fs", "fsck.f2fs", "fsck.f2fs", "dump.f2fs", "resize.f2fs",
+             "mkfs.f2fs", "fsck.f2fs", "dump.f2fs", "resize.f2fs",
              "mkfs.nilfs2", "nilfs-tune", "nilfs-resize",
              "mkntfs", "ntfsfix", "ntfsresize", "ntfslabel", "ntfsinfo",
              "mkfs.vfat", "fatlabel", "fsck.vfat", "vfat-resize",
@@ -231,11 +231,86 @@ def delete_lio_device(dev_path):
     else:
         raise RuntimeError("Unknown device '%s'" % dev_path)
 
-def find_nvme_ctrl_devs_for_subnqn(subnqn):
+
+def setup_scsi_debug():
+    res, out, err = run_command('modprobe scsi_debug')
+    if res != 0:
+        raise RuntimeError("Failed to load scsi_debug module: %s %s" % (out, err))
+    dirs = []
+    n_tries = 0
+    while len(dirs) < 1 and n_tries < 5:
+        dirs = glob.glob('/sys/bus/pseudo/drivers/scsi_debug/adapter*/host*/target*/*:*/block')
+        time.sleep(0.5)
+        n_tries += 1
+    if len(dirs) < 1:
+        raise RuntimeError("Failed to setup SCSI debug device for testing")
+    scsi_debug_dev = os.listdir(dirs[0])
+    if len(scsi_debug_dev) != 1:
+        raise RuntimeError("Failed to setup SCSI debug device for testing")
+
+    scsi_debug_dev = '/dev/' + scsi_debug_dev[0]
+    if not os.path.exists(scsi_debug_dev):
+        raise RuntimeError("Failed to setup SCSI debug device for testing")
+
+    return scsi_debug_dev
+
+
+def clean_scsi_debug(scsi_debug_dev):
+    try:
+        device = scsi_debug_dev.split('/')[-1]
+        if os.path.exists('/sys/block/' + device):
+            write_file('/sys/block/%s/device/delete' % device, '1')
+        n_tries = 0
+        while os.path.exists(device) and n_tries < 5:
+            time.sleep(0.5)
+            n_tries += 1
+        n_tries = 0
+        while n_tries < 5:
+            res, _out, _err = run_command('modprobe -r scsi_debug')
+            if res == 0:
+                break
+            time.sleep(0.5)
+            n_tries += 1
+    except:
+        pass
+
+def _wait_for_nvme_controllers_ready(subnqn, timeout=3):
+    """
+    Wait for NVMe controllers with matching subsystem NQN to be in live state
+    
+    :param str subnqn: subsystem nqn to match controllers against
+    :param int timeout: timeout in seconds (default: 3)
+    """
+    start_time = time.time()
+    
+    while time.time() - start_time < timeout:
+        try:
+            for ctrl_path in glob.glob("/sys/class/nvme/nvme*/"):
+                state_file = os.path.join(ctrl_path, "state")
+                subsysnqn_file = os.path.join(ctrl_path, "subsysnqn")
+                try:
+                    state = read_file(state_file).strip()
+                    controller_subnqn = read_file(subsysnqn_file).strip()
+                    if state == "live" and controller_subnqn == subnqn:
+                        # Found a matching live controller
+                        os.system("udevadm settle")
+                        return
+                except:
+                    continue
+                
+        except:
+            pass
+        
+        time.sleep(1)
+    
+    os.system("udevadm settle")
+
+def find_nvme_ctrl_devs_for_subnqn(subnqn, wait_for_ready=True):
     """
     Find NVMe controller devices for the specified subsystem nqn
 
     :param str subnqn: subsystem nqn
+    :param bool wait_for_ready: whether to wait for controllers to be ready (default: True)
     """
 
     def _check_subsys(subsys, dev_paths):
@@ -252,6 +327,9 @@ def find_nvme_ctrl_devs_for_subnqn(subnq
                 except:
                     pass
 
+    # Wait for controllers to be ready if requested
+    if wait_for_ready:
+        _wait_for_nvme_controllers_ready(subnqn)
     ret, out, err = run_command("nvme list --output-format=json --verbose")
     if ret != 0:
         raise RuntimeError("Error getting NVMe list: '%s %s'" % (out, err))
@@ -274,11 +352,12 @@ def find_nvme_ctrl_devs_for_subnqn(subnq
     return dev_paths
 
 
-def find_nvme_ns_devs_for_subnqn(subnqn):
+def find_nvme_ns_devs_for_subnqn(subnqn, wait_for_ready=True):
     """
     Find NVMe namespace block devices for the specified subsystem nqn
 
     :param str subnqn: subsystem nqn
+    :param bool wait_for_ready: whether to wait for controllers to be ready (default: True)
     """
 
     def _check_namespaces(node, ns_dev_paths):
@@ -301,6 +380,8 @@ def find_nvme_ns_devs_for_subnqn(subnqn)
                     if 'Namespaces' in ctrl:
                         _check_namespaces(ctrl, ns_dev_paths)
 
+    if wait_for_ready:
+        _wait_for_nvme_controllers_ready(subnqn)
     ret, out, err = run_command("nvme list --output-format=json --verbose")
     if ret != 0:
         raise RuntimeError("Error getting NVMe list: '%s %s'" % (out, err))
