From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 656911FF0DF for ; Fri, 28 Aug 2026 15:31:49 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 872122164E; Fri, 28 Aug 2026 15:31:12 +0200 (CEST) From: Shannon Sterz To: pve-devel@lists.proxmox.com Subject: [PATCH manager 11/21] api: host backup: add warnings in case zfs snapdir is disabled Date: Fri, 28 Aug 2026 15:30:20 +0200 Message-ID: <20260828133030.351140-12-s.sterz@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260828133030.351140-1-s.sterz@proxmox.com> References: <20260828133030.351140-1-s.sterz@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1787923824688 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.909 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: VOV33IVU4YP4VCB2ZXQR7V73AGMIHZZF X-Message-ID-Hash: VOV33IVU4YP4VCB2ZXQR7V73AGMIHZZF X-MailFrom: s.sterz@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: queries the zfs property `snapdir` and adds a warning to the logs that backup consistency can be improved if users enable it. note that this is mostly an additional measure, the default is 'hidden' which means that the snapshot directory is enabled but hidden. Signed-off-by: Shannon Sterz --- PVE/API2/HostBackup.pm | 24 ++++++++++++++++++++---- PVE/HostBackupTools.pm | 13 ++++++++++++- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/PVE/API2/HostBackup.pm b/PVE/API2/HostBackup.pm index 8e8a87e10..60b07773b 100644 --- a/PVE/API2/HostBackup.pm +++ b/PVE/API2/HostBackup.pm @@ -47,12 +47,27 @@ my sub prepare_backup($backup_name) { if (my $mntinfo = PVE::VZDump::get_mount_info("/")) { if ($mntinfo->{fstype} eq "zfs") { - $snap_cmd = ['zfs', 'snapshot', $mntinfo->{device} . '@' . $backup_name]; - # snapshots are mounted under `/.zfs/snapshots` unless snapdir is set to "disabled". # the default is "hidden". # https://openzfs.github.io/openzfs-docs/man/v2.4/7/zfsprops.7.html#snapdir - $base_path = "/.zfs/snapshot/$backup_name/"; + my $output = PVE::HostBackupTools::get_json_from_command([ + "zfs", "get", "snapdir", "rpool/ROOT/pve-1", "-j", + ]); + + my $snapdir_opt = + $output->{datasets}->{"rpool/ROOT/pve-1"}->{properties}->{snapdir}->{value}; + + warn "could not determine if zfs supports mounted root fs snapshots, continuing on a" + . " best effort basis.\n" + if !defined($snapdir_opt); + + if ($snapdir_opt eq "disabled") { + warn "zfs property 'snapdir' is disabled. please enable it or set it to 'hidden'" + . " to allow for more consistent host backups!\n"; + } elsif (-d "/.zfs/snapshot/") { + $snap_cmd = ['zfs', 'snapshot', $mntinfo->{device} . '@' . $backup_name]; + $base_path = "/.zfs/snapshot/$backup_name/"; + } } elsif ($mntinfo->{fstype} eq "btrfs") { # check if snapshots directory exists, if not create one mkdir "/.snapshots" @@ -171,7 +186,8 @@ my sub cleanup_backup($backup_name, $backup_target) { if (my $mntinfo = PVE::VZDump::get_mount_info("/")) { if ($mntinfo->{fstype} eq "zfs") { - $snap_cmd = ['zfs', 'destroy', $mntinfo->{device} . '@' . $backup_name]; + $snap_cmd = ['zfs', 'destroy', $mntinfo->{device} . '@' . $backup_name] + if -d "/.zfs/snapshot/"; } elsif ($mntinfo->{fstype} eq "btrfs") { $snap_cmd = [ 'btrfs', '-q', 'subvolume', 'delete', '--', '/.snapshots/root@' . $backup_name, diff --git a/PVE/HostBackupTools.pm b/PVE/HostBackupTools.pm index c51c78d9b..9ac143f8b 100644 --- a/PVE/HostBackupTools.pm +++ b/PVE/HostBackupTools.pm @@ -13,8 +13,19 @@ use PVE::Tools; use PVE::Cmd qw(run); use PVE::VZDump; -my sub get_json_from_command($command) { +=head3 get_json_from_command($command) +Executes the provided C using C and assumes that the +output is valid JSON in its entirety. Output can be split across lines, but may +not be interrupted by other text or contain more than one valid JSON array +or object. The caller is required to make sure that the command's output conforms +to these restrictions. + +Returns a parsed hash or array of the JSON output of the command. + +=cut + +sub get_json_from_command($command) { my $json = ''; run( -- 2.47.3