From cfa9b5c082972b5b47cd03810d5bfe7602aa05ad Mon Sep 17 00:00:00 2001 From: Zachary Fogg Date: Thu, 4 Dec 2025 09:44:29 -0500 Subject: [PATCH] Check if qgroup exists before attempting to destroy it When a btrfs subvolume is deleted, its associated qgroup is often automatically destroyed by btrfs. Timeshift would then attempt to explicitly destroy the qgroup and fail with an error message, even though the operation was successful. This change checks if the qgroup still exists before attempting to destroy it, eliminating the spurious error message. If the qgroup is already gone (auto-destroyed with the subvolume), we log a debug message instead. If the qgroup still exists, we attempt to destroy it and handle any race condition gracefully. Fixes the "Failed to destroy qgroup" error that occurs during snapshot deletion when quotas are enabled. Related: #354, #437 --- src/Core/Subvolume.vala | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Core/Subvolume.vala b/src/Core/Subvolume.vala index 27ef3ab9..1520a1b1 100644 --- a/src/Core/Subvolume.vala +++ b/src/Core/Subvolume.vala @@ -193,20 +193,31 @@ public class Subvolume : GLib.Object{ sleep(1000); } - log_msg("%s: 0/%ld".printf(_("Destroying qgroup"), id)); - - cmd = "btrfs qgroup destroy 0/%ld '%s'".printf(id, repo.mount_paths[name]); + // Check if qgroup still exists before trying to destroy it. + // The qgroup may have been auto-destroyed when the subvolume was deleted. + string qgroup_id = "0/%ld".printf(id); + cmd = "btrfs qgroup show -f '%s'".printf(repo.mount_paths[name]); log_debug(cmd); ret_val = exec_sync(cmd, out std_out, out std_err); - if (ret_val != 0){ - log_error(_("Failed to destroy qgroup") + ": '0/%ld'".printf(id)); - // the subvolume is gone now. So this can be called a success. - return true; + bool qgroup_exists = (ret_val == 0) && (std_out.contains(qgroup_id)); + + if (!qgroup_exists) { + log_debug("Qgroup %s already removed (auto-destroyed with subvolume)".printf(qgroup_id)); + } else { + log_msg("%s: %s".printf(_("Destroying qgroup"), qgroup_id)); + + cmd = "btrfs qgroup destroy %s '%s'".printf(qgroup_id, repo.mount_paths[name]); + log_debug(cmd); + ret_val = exec_sync(cmd, out std_out, out std_err); + if (ret_val != 0){ + // Qgroup may have been destroyed between our check and destroy attempt + log_debug("Qgroup %s could not be destroyed (likely already removed): %s".printf(qgroup_id, std_err)); + } else { + log_msg("%s: %s\n".printf(_("Destroyed qgroup"), qgroup_id)); + } } - log_msg("%s: 0/%ld\n".printf(_("Destroyed qgroup"), id)); - log_debug("Rescanning quotas (post)..."); while (exec_sync("btrfs quota rescan %s".printf(mount_path), out std_out, out std_err) != 0) { log_debug("Still rescanning quotas (post)... %s".printf(std_err));