/*
- * Copyright (c) 2024 SUSE LLC
+ * Copyright (c) [2024-2025] SUSE LLC
*
* All Rights Reserved.
*
get_child_value(json_file.get_root(), "ssh-identity", ssh_identity);
}
+ get_child_value(json_file.get_root(), "send-compressed-data", send_compressed_data);
+ get_child_nodes(json_file.get_root(), "send-options", send_options);
+ get_child_nodes(json_file.get_root(), "receive-options", receive_options);
+
get_child_value(json_file.get_root(), "target-btrfs-bin", target_btrfs_bin);
get_child_value(json_file.get_root(), "target-findmnt-bin", target_findmnt_bin);
get_child_value(json_file.get_root(), "target-mkdir-bin", target_mkdir_bin);
Shell get_source_shell() const;
Shell get_target_shell() const;
+ bool send_compressed_data = true;
+ vector<string> send_options;
+ vector<string> receive_options;
+
string target_btrfs_bin = BTRFS_BIN;
string target_findmnt_bin = FINDMNT_BIN;
string target_mkdir_bin = MKDIR_BIN;
/*
* Copyright (c) [2004-2015] Novell, Inc.
- * Copyright (c) [2017-2024] SUSE LLC
+ * Copyright (c) [2017-2025] SUSE LLC
*
* All Rights Reserved.
*
*/
+#include <sys/utsname.h>
#include <cstring>
#include <regex>
#include <boost/algorithm/string.hpp>
return s;
}
+
+ void
+ CmdBtrfsVersion::query_version()
+ {
+ if (did_set_version)
+ return;
+
+ SystemCmd::Args cmd_args = { btrfs_bin, "--version" };
+ SystemCmd cmd(shellify(shell, cmd_args));
+
+ if (cmd.retcode() != 0)
+ {
+ y2err("command '" << cmd.cmd() << "' failed: " << cmd.retcode());
+ for (const string& tmp : cmd.get_stdout())
+ y2err(tmp);
+ for (const string& tmp : cmd.get_stderr())
+ y2err(tmp);
+
+ SN_THROW(Exception("'btrfs --version' failed"));
+ }
+
+ parse_version(cmd.get_stdout()[0]);
+ }
+
+
+ void
+ CmdBtrfsVersion::parse_version(const string& version)
+ {
+ // example versions: "5.14 " (yes, with a trailing space), "6.0", "6.0.2"
+ const regex version_rx("btrfs-progs v([0-9]+)\\.([0-9]+)(\\.([0-9]+))?( )*", regex::extended);
+
+ smatch match;
+
+ if (!regex_match(version, match, version_rx))
+ SN_THROW(Exception("failed to parse btrfs version '" + version + "'"));
+
+ major = stoi(match[1]);
+ minor = stoi(match[2]);
+ patchlevel = match[4].length() == 0 ? 0 : stoi(match[4]);
+
+ y2mil("major:" << major << " minor:" << minor << " patchlevel:" << patchlevel);
+
+ did_set_version = true;
+ }
+
+
+ int
+ CmdBtrfsVersion::supported_proto()
+ {
+ query_version();
+
+ if (major >= 6)
+ return 2;
+
+ return 1;
+ }
+
+
+ int
+ Uname::supported_proto()
+ {
+ const regex release_rx("^([0-9]+)\\.([0-9]+).*", regex::extended);
+
+ struct utsname buffer;
+
+ if (uname(&buffer) < 0)
+ SN_THROW(Exception("syscall uname failed"));
+
+ cmatch match;
+
+ if (!regex_match(buffer.release, match, release_rx))
+ SN_THROW(Exception("failed to parse uname release '" + string(buffer.release) + "'"));
+
+ int major = stoi(match[1]);
+ int minor = stoi(match[2]);
+
+ y2mil("major:" << major << " minor:" << minor);
+
+ if (major >= 6)
+ return 2;
+
+ return 1;
+ }
+
}
/*
* Copyright (c) [2004-2015] Novell, Inc.
- * Copyright (c) [2017-2024] SUSE LLC
+ * Copyright (c) [2017-2025] SUSE LLC
*
* All Rights Reserved.
*
};
+
+ /**
+ * Lazy query of btrfs command version.
+ */
+ class CmdBtrfsVersion
+ {
+ public:
+
+ CmdBtrfsVersion(const string& btrfs_bin, const Shell& shell)
+ : btrfs_bin(btrfs_bin), shell(shell)
+ {}
+
+ int supported_proto();
+
+ private:
+
+ void query_version();
+ void parse_version(const string& version);
+
+ const string btrfs_bin;
+ const Shell shell;
+
+ bool did_set_version = false;
+
+ int major = 0;
+ int minor = 0;
+ int patchlevel = 0;
+
+ };
+
+
+ class Uname
+ {
+ public:
+
+ static int supported_proto();
+
+ };
+
}
#endif
*/
-#include <stdio.h>
+#include <cstdio>
#include <sys/stat.h>
#include <functional>
#include <memory>
}
+ bool
+ get_child_nodes(json_object* parent, const char* name, vector<string>& values)
+ {
+ vector<json_object*> children;
+
+ if (!get_child_nodes(parent, name, children))
+ return false;
+
+ values.clear();
+
+ for (json_object* child : children)
+ {
+ if (!json_object_is_type(child, json_type_string))
+ return false;
+
+ values.push_back(json_object_get_string(child));
+ }
+
+ return true;
+ }
+
+
bool
get_child_nodes(json_object* parent, const char* name, vector<json_object*>& children)
{
/*
- * Copyright (c) [2017-2024] SUSE LLC
+ * Copyright (c) [2017-2025] SUSE LLC
*
* All Rights Reserved.
*
get_child_value(json_object* parent, const char* name, Type& value);
+ bool
+ get_child_nodes(json_object* parent, const char* name, vector<string>& values);
+
bool
get_child_nodes(json_object* parent, const char* name, vector<json_object*>& children);
void
- TheBigThing::transfer(const BackupConfig& backup_config, const TheBigThings& the_big_things,
+ TheBigThing::transfer(const BackupConfig& backup_config, TheBigThings& the_big_things,
bool quiet)
{
if (!quiet)
// Copy snapshot to target.
+ const int proto = std::min({
+ Uname::supported_proto(),
+ the_big_things.source_btrfs_version.supported_proto(),
+ the_big_things.target_btrfs_version.supported_proto()
+ });
+
TheBigThings::const_iterator it1 = the_big_things.find_send_parent(*this);
SystemCmd::Args cmd3a_args = { BTRFS_BIN, "send" };
+ if (proto >= 2)
+ cmd3a_args << "--proto" << to_string(proto);
+ if (backup_config.send_compressed_data && proto >= 2)
+ cmd3a_args << "--compressed-data";
+ cmd3a_args << backup_config.send_options;
+
if (it1 != the_big_things.end())
cmd3a_args << "-p" << backup_config.source_path + "/" SNAPSHOTS_NAME "/" +
to_string(it1->num) + "/" SNAPSHOT_NAME;
cmd3a_args << "--" << backup_config.source_path + "/" SNAPSHOTS_NAME "/" + num_string + "/" SNAPSHOT_NAME;
- SystemCmd::Args cmd3b_args = { backup_config.target_btrfs_bin, "receive", "--",
- backup_config.target_path + "/" + num_string };
+ SystemCmd::Args cmd3b_args = { backup_config.target_btrfs_bin, "receive" };
+ cmd3b_args << backup_config.receive_options;
+ cmd3b_args << "--" << backup_config.target_path + "/" + num_string;
y2deb("source: " << cmd3a_args.get_values());
y2deb("target: " << cmd3b_args.get_values());
TheBigThings::TheBigThings(const BackupConfig& backup_config, ProxySnappers* snappers, bool verbose)
- : snapper(snappers->getSnapper(backup_config.config)), locker(snapper)
+ : source_btrfs_version(BTRFS_BIN, backup_config.get_source_shell()),
+ target_btrfs_version(backup_config.target_btrfs_bin, backup_config.get_target_shell()),
+ snapper(snappers->getSnapper(backup_config.config)), locker(snapper)
{
if (backup_config.source_path != snapper->getConfig().getSubvolume())
{
/*
- * Copyright (c) 2024 SUSE LLC
+ * Copyright (c) [2024-2025] SUSE LLC
*
* All Rights Reserved.
*
#include "../proxy/proxy.h"
#include "../proxy/locker.h"
+#include "CmdBtrfs.h"
+
namespace snapper
{
TheBigThing(unsigned int num) : num(num) {}
- void transfer(const BackupConfig& backup_config, const TheBigThings& the_big_things, bool quiet);
+ void transfer(const BackupConfig& backup_config, TheBigThings& the_big_things, bool quiet);
void remove(const BackupConfig& backup_config, bool quiet);
*/
const_iterator find_send_parent(const TheBigThing& the_big_thing) const;
+ CmdBtrfsVersion source_btrfs_version;
+ CmdBtrfsVersion target_btrfs_version;
+
private:
const ProxySnapper* snapper;
<refentry id='snapper-backup-configs5'>
<refentryinfo>
- <date>2025-04-09</date>
+ <date>2025-10-07</date>
</refentryinfo>
<refmeta>
<refentrytitle>snapper-backup-configs</refentrytitle>
<manvolnum>5</manvolnum>
- <refmiscinfo class='date'>2025-04-09</refmiscinfo>
+ <refmiscinfo class='date'>2025-10-07</refmiscinfo>
<refmiscinfo class='version'>@VERSION@</refmiscinfo>
<refmiscinfo class='manual'>Filesystem Snapshot Management</refmiscinfo>
</refmeta>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>send-compressed-data</option></term>
+ <listitem>
+ <para>Per default snbk will add the option --compressed-data iff
+ supported by the source and target system. This can be disable by
+ setting this value to false.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>send-options</option></term>
+ <listitem>
+ <para>Extra options for the btrfs send command as an array
+ of strings. Optional.</para>
+ <para>Per default snbk automatically adds --proto and --compressed-data
+ iff supported by both the source and target.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>receive-options</option></term>
+ <listitem>
+ <para>Extra options for the btrfs receive command as an
+ array of strings. Optional.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>target-btrfs-bin</option></term>
<listitem>
+-------------------------------------------------------------------
+Tue Oct 07 14:42:16 CEST 2025 - aschnell@suse.com
+
+- support sending compressed data in snbk
+ (gh#openSUSE/snapper#1059)
+
-------------------------------------------------------------------
Wed Sep 24 16:12:34 CEST 2025 - aschnell@suse.com
#!/bin/bash
+if [ "$#" -eq 1 ] && [ "$1" == "--version" ]; then
+ exec sudo /usr/sbin/btrfs --version
+fi
+
if [ "$#" -lt 3 ]; then
echo "Usage: $0 <group> [subcommand] [options] -- /backup/..."
exit 1