]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- added new workflow using only two steps to installation-helper 964/head
authorArvin Schnell <aschnell@suse.de>
Thu, 12 Dec 2024 17:00:33 +0000 (18:00 +0100)
committerArvin Schnell <aschnell@suse.de>
Thu, 12 Dec 2024 17:00:33 +0000 (18:00 +0100)
client/installation-helper/Makefile.am
client/installation-helper/installation-helper.cc
client/installation-helper/readme.txt [new file with mode: 0644]
client/installation-helper/test1.sh [new file with mode: 0755]
client/installation-helper/test2.sh [new file with mode: 0755]
package/snapper.changes

index bc50f1e04f251d7d9ae4ee99ca74676b6c9d4920..022bc8cd6ba7287b0348d887c5d5e10cb595318c 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile.am for snapper/client/installation-helper
 #
 
-AM_CPPFLAGS = -I$(top_srcdir)
+AM_CPPFLAGS = -I$(top_srcdir) $(XML2_CFLAGS)
 
 libexecdir = /usr/lib/snapper
 
index 5ff13f9dba3428d14c03ff997fb3d2a3da8ad1b1..ac30b1842754693004beed51975e908f2985f4d2 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2015 Novell, Inc.
- * Copyright (c) [2018-2022] SUSE LLC
+ * Copyright (c) [2018-2024] SUSE LLC
  *
  * All Rights Reserved.
  *
 
 #include <cstdlib>
 #include <unistd.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <iostream>
-#include <boost/algorithm/string.hpp>
 
 #include <snapper/Snapper.h>
 #include <snapper/AppUtil.h>
@@ -37,6 +37,7 @@
 #include <snapper/FileUtils.h>
 #include <snapper/PluginsImpl.h>
 #include "snapper/Log.h"
+#include "snapper/XmlFile.h"
 
 #include "../utils/GetOpts.h"
 
@@ -265,6 +266,239 @@ step5(const string& root_prefix, const string& snapshot_type, unsigned int pre_n
 }
 
 
+// Without the rollback support of libsnapper some btrfs utils are undefined.
+#ifdef ENABLE_ROLLBACK
+
+
+bool
+step_filesystem(const string& root_prefix)
+{
+    // create subvolume /<root-prefix>/.snapshots
+
+    cout << "creating btrfs subvolume /<root-prefix>/.snapshots" << endl;
+
+    try
+    {
+       int fd = open(prepend_root_prefix(root_prefix, "/").c_str(),
+                     O_RDONLY | O_NOATIME | O_CLOEXEC);
+       if (fd < 0)
+           SN_THROW(Exception("open failed"));
+
+       create_subvolume(fd, ".snapshots");
+
+       close(fd);
+    }
+    catch (const exception& e)
+    {
+       cerr << "creating /<root-prefix>/.snapshots failed" << endl;
+
+       return false;
+    }
+
+    // create /<root-prefix>/.snapshots/1
+
+    cout << "creating directory /<root-prefix>/.snapshots/1" << endl;
+
+    try
+    {
+       if (mkdir(prepend_root_prefix(root_prefix, "/.snapshots/1").c_str(), 0777) != 0)
+           SN_THROW(Exception("mkdir failed"));
+    }
+    catch (const exception& e)
+    {
+       cerr << "creating /<root-prefix>/.snapshots/1 failed" << endl;
+
+       return false;
+    }
+
+    // create btrfs subvolume /<root-prefix>/.snapshots/1/snapshot
+
+    cout << "creating btrfs subvolume /<root-prefix>/.snapshots/1/snapshot" << endl;
+
+    try
+    {
+       int fd = open(prepend_root_prefix(root_prefix, "/.snapshots/1").c_str(),
+                     O_RDONLY | O_NOATIME | O_CLOEXEC);
+       if (fd < 0)
+           SN_THROW(Exception("open failed"));
+
+       create_subvolume(fd, "snapshot");
+
+       close(fd);
+    }
+    catch (const exception& e)
+    {
+       cerr << "creating /<root-prefix>/.snapshots/1/snapshot failed" << endl;
+
+       return false;
+    }
+
+    // set default btrfs subvolume to /<root-prefix>/.snapshots/1/snapshot
+
+    cout << "setting default btrfs subvolume to /<root-prefix>/.snapshots/1/snapshot" << endl;
+
+    try
+    {
+       int fd = open(prepend_root_prefix(root_prefix, "/.snapshots/1/snapshot").c_str(),
+                     O_RDONLY | O_NOATIME | O_CLOEXEC);
+       if (fd < 0)
+           SN_THROW(Exception("open failed"));
+
+       subvolid_t id = get_id(fd);
+
+       set_default_id(fd, id);
+
+       close(fd);
+    }
+    catch (const exception& e)
+    {
+       cerr << "setting default subvolume failed" << endl;
+
+       return false;
+    }
+
+    // create /<root-prefix>/.snapshots/1/snapshot/.snapshots
+
+    cout << "creating directory /<root-prefix>/.snapshots/1/snapshot/.snapshots" << endl;
+
+    try
+    {
+       if (mkdir(prepend_root_prefix(root_prefix, "/.snapshots/1/snapshot/.snapshots").c_str(), 0777) != 0)
+           SN_THROW(Exception("mkdir failed"));
+    }
+    catch (const exception& e)
+    {
+       cerr << "creating /<root-prefix>/.snapshots/1/snapshot/.snapshots failed" << endl;
+
+       return false;
+    }
+
+    return true;
+}
+
+
+bool
+step_config(const string& root_prefix, const string& description, const string& cleanup,
+           const map<string, string>& userdata)
+{
+    // create directories
+
+    cout << "creating directories in /<root-prefix>" << endl;
+
+    mkdir(prepend_root_prefix(root_prefix, "/etc").c_str(), 0777);
+    mkdir(prepend_root_prefix(root_prefix, "/etc/sysconfig").c_str(), 0777);
+    mkdir(prepend_root_prefix(root_prefix, "/etc/snapper").c_str(), 0777);
+    mkdir(prepend_root_prefix(root_prefix, "/etc/snapper/configs").c_str(), 0777);
+
+    // create snapper sysconfig /<root-prefix>/etc/sysconfig/snapper
+
+    cout << "creating snapper sysconfig /<root-prefix>/etc/sysconfig/snapper" << endl;
+
+    try
+    {
+       /*
+       SysconfigFile sysconfig;        // TODO, add in class SysconfigFile
+
+       sysconfig.set_name(prepend_root_prefix(root_prefix, SYSCONFIG_FILE));
+
+       sysconfig.set_value("SNAPPER_CONFIGS", "root");
+
+       sysconfig.save();
+       */
+
+       AsciiFileWriter sysconfig(prepend_root_prefix(root_prefix, SYSCONFIG_FILE), Compression::NONE);
+
+       sysconfig.write_line("SNAPPER_CONFIGS=\"root\"");
+
+       sysconfig.close();
+    }
+    catch (const Exception& e)
+    {
+       cerr << "setup of /<root-prefix>/etc/sysconfig/snapper failed, "
+            << e.what() << endl;
+
+       return false;
+    }
+
+    // create snapper config /<root-prefix>/etc/snapper/configs/root
+
+    cout << "creating snapper config /<root-prefix>/etc/snapper/configs/root" << endl;
+
+    try
+    {
+       string template_file = locate_file("default", ETC_CONFIG_TEMPLATE_DIR, USR_CONFIG_TEMPLATE_DIR);
+
+       SysconfigFile config(template_file);
+
+       config.set_name(prepend_root_prefix(root_prefix, CONFIGS_DIR "/root"));
+
+       config.set_value(KEY_SUBVOLUME, "/");
+       config.set_value(KEY_FSTYPE, "btrfs");
+
+       config.save();
+    }
+    catch (const Exception& e)
+    {
+       cerr << "setup of /<root-prefix>/etc/snapper/configs/root failed, "
+            << e.what() << endl;
+
+       return false;
+    }
+
+    // create info file /<root-prefix>/.snapshots/1/info.xml
+
+    cout << "creating snapper info file /<root-prefix>/.snapshots/1/info.xml" << endl;
+
+    try
+    {
+       XmlFile xml;
+       xmlNode* node = xmlNewNode("snapshot");
+       xml.setRootElement(node);
+
+       setChildValue(node, "type", toString(SINGLE));
+
+       setChildValue(node, "num", 1);
+
+       setChildValue(node, "date", datetime(time(NULL), true, true));
+
+       if (!description.empty())
+           setChildValue(node, "description", description);
+
+       if (!cleanup.empty())
+           setChildValue(node, "cleanup", cleanup);
+
+       for (const map<string, string>::value_type& tmp : userdata)
+       {
+           xmlNode* userdata_node = xmlNewChild(node, "userdata");
+           setChildValue(userdata_node, "key", tmp.first);
+           setChildValue(userdata_node, "value", tmp.second);
+       }
+
+       int fd = open(prepend_root_prefix(root_prefix, "/.snapshots/1/info.xml").c_str(),
+                     O_RDWR | O_CREAT | O_CLOEXEC, 0666);
+       if (fd < 0)
+           SN_THROW(Exception("open failed"));
+
+       fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       xml.save(fd);
+    }
+    catch (const Exception& e)
+    {
+       SN_CAUGHT(e);
+
+       cerr << "setup of /<root-prefix>/.snapshots/1/info.xml failed, "
+            << e.what() << endl;
+
+       return false;
+    }
+
+    return true;
+}
+
+#endif
+
+
 void
 log_do(LogLevel level, const string& component, const char* file, const int line, const char* func,
        const string& text)
@@ -362,4 +596,14 @@ main(int argc, char** argv)
        step4();
     else if (step == "5")
        return step5(root_prefix, snapshot_type, pre_num, description, cleanup, userdata) ? EXIT_SUCCESS : EXIT_FAILURE;
+
+#ifdef ENABLE_ROLLBACK
+
+    else if (step == "filesystem")
+       return step_filesystem(root_prefix) ? EXIT_SUCCESS : EXIT_FAILURE;
+    else if (step == "config")
+       return step_config(root_prefix, description, cleanup, userdata) ? EXIT_SUCCESS : EXIT_FAILURE;
+
+#endif
+
 }
diff --git a/client/installation-helper/readme.txt b/client/installation-helper/readme.txt
new file mode 100644 (file)
index 0000000..3feba9e
--- /dev/null
@@ -0,0 +1,36 @@
+
+The "old" way using the steps 1 to 5 is deprecated.
+
+
+The "new" way is to use the two steps "filesystem" and "config".
+
+The step "filesystem" does the following:
+
+  create btrfs subvolume /<root-prefix>/.snapshots
+  create directory /<root-prefix>/.snapshots/1
+  create btrfs subvolume /<root-prefix>/.snapshots/1/snapshot
+  set default btrfs subvolume to /<root-prefix>/.snapshots/1/snapshot
+  create directory /<root-prefix>/.snapshots/1/snapshot/.snapshots
+
+The step "config" does the following:
+
+  create snapper sysconfig /<root-prefix>/etc/sysconfig/snapper
+  create snapper config /<root-prefix>/etc/snapper/configs/root
+  create snapper info file /<root-prefix>/.snapshots/1/info.xml
+
+The installer has to mount the filesystem before the step
+"filesystem". Between the two steps the filesystem must be remounted
+(since the default subvolume was changes). Additionally the .snapshots
+subvolume must be mounted by the installer.
+
+The installer must also handle /etc/fstab or similar.
+
+Works with and without the optional "@" subvolume.
+
+No plugins are run.
+
+Example workflow can be seen in test1.sh and test2.sh.
+
+The "filesystem" step can of course be implemented somewhere
+else. E.g. libstorage-ng is capable of doing this (see example there).
+
diff --git a/client/installation-helper/test1.sh b/client/installation-helper/test1.sh
new file mode 100755 (executable)
index 0000000..be501c5
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/bash -x
+
+umount /test/.snapshots
+umount /test
+
+mkfs.btrfs -f /dev/sdc1
+
+mount /dev/sdc1 /test
+
+/usr/lib/snapper/installation-helper --root-prefix /test --step filesystem
+
+umount /test
+
+mount /dev/sdc1 /test
+mount /dev/sdc1 -o subvol=.snapshots /test/.snapshots
+
+/usr/lib/snapper/installation-helper --root-prefix /test --step config --description initial --userdata a=1
+
+snapper --no-dbus --root /test ls
+
diff --git a/client/installation-helper/test2.sh b/client/installation-helper/test2.sh
new file mode 100755 (executable)
index 0000000..309a01d
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/bash -x
+
+umount /test/.snapshots
+umount /test
+
+mkfs.btrfs -f /dev/sdc1
+
+mount /dev/sdc1 /test
+
+btrfs subvolume create /test/@
+btrfs subvolume set-default /test/@
+
+umount /test
+
+mount /dev/sdc1 /test
+
+/usr/lib/snapper/installation-helper --root-prefix /test --step filesystem
+
+umount /test
+
+mount /dev/sdc1 /test
+mount /dev/sdc1 -o subvol=@/.snapshots /test/.snapshots
+
+/usr/lib/snapper/installation-helper --root-prefix /test --step config --description initial --userdata a=1
+
+snapper --no-dbus --root /test ls
+
index 2e0eddaac9b0343b61f1ddf5de489e6ce43698cb..31c950d429d1e895a263504dd8341fada8f21105 100644 (file)
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Mon Dec 02 16:51:50 CET 2024 - aschnell@suse.com
+
+- added new workflow using only two steps to installation-helper
+  (gh#openSUSE/snapper#944)
+
 -------------------------------------------------------------------
 Tue Nov 05 10:50:50 CET 2024 - aschnell@suse.com