Hello community, here is the log from the commit of package transactional-update for openSUSE:Factory checked in at 2018-04-25 10:01:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/transactional-update (Old) and /work/SRC/openSUSE:Factory/.transactional-update.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "transactional-update" Wed Apr 25 10:01:55 2018 rev:26 rq:600622 version:2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/transactional-update/transactional-update.changes 2018-04-22 14:40:26.123240052 +0200 +++ /work/SRC/openSUSE:Factory/.transactional-update.new/transactional-update.changes 2018-04-25 10:02:00.753387693 +0200 @@ -1,0 +2,7 @@ +Tue Apr 24 14:23:41 CEST 2018 - ku...@suse.de + +- Update to version 2.0 + - Create missing directories from rpm database during boot + - Merge /etc overlay with root subvolume during update + +------------------------------------------------------------------- Old: ---- transactional-update-1.29.tar.bz2 New: ---- transactional-update-2.0.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ transactional-update.spec ++++++ --- /var/tmp/diff_new_pack.ds9jfE/_old 2018-04-25 10:02:02.329329871 +0200 +++ /var/tmp/diff_new_pack.ds9jfE/_new 2018-04-25 10:02:02.333329724 +0200 @@ -17,20 +17,25 @@ Name: transactional-update -Version: 1.29 +Version: 2.0 Release: 0 Summary: Transactional Updates with btrfs and snapshots License: GPL-2.0-or-later Group: System/Base -Url: https://github.com/thkukuk/transactional-update +URL: https://github.com/thkukuk/transactional-update Source: %{name}-%{version}.tar.bz2 BuildRequires: fdupes BuildRequires: gcc-c++ +BuildRequires: pkgconfig +BuildRequires: rpm-devel BuildRequires: pkgconfig(libzypp) Requires: logrotate Requires: lsof # psmisc is needed because of fuser +Requires: perl-HTML-Parser Requires: psmisc +Requires: rsync +Conflicts: snapper-zypp-plugin Recommends: rebootmgr %description @@ -49,16 +54,16 @@ %fdupes %{buildroot}%{_mandir} %pre -%service_add_pre %{name}.service %{name}.timer +%service_add_pre %{name}.service %{name}.timer create-dirs-from-rpmdb.service %post -%service_add_post %{name}.service %{name}.timer +%service_add_post %{name}.service %{name}.timer create-dirs-from-rpmdb.service %preun -%service_del_preun %{name}.service %{name}.timer +%service_del_preun %{name}.service %{name}.timer create-dirs-from-rpmdb.service %postun -%service_del_postun %{name}.service %{name}.timer +%service_del_postun %{name}.service %{name}.timer create-dirs-from-rpmdb.service %files %license COPYING @@ -66,14 +71,19 @@ %config(noreplace) %{_sysconfdir}/logrotate.d/transactional-update %{_unitdir}/transactional-update.service %{_unitdir}/transactional-update.timer +%{_unitdir}/create-dirs-from-rpmdb.service +%{_sbindir}/create_dirs_from_rpmdb %{_sbindir}/transactional-update %{_sbindir}/tu-rebuild-kdump-initrd %{_sbindir}/transactional-update-helper %dir %{_prefix}%{_sysconfdir} %{_prefix}%{_sysconfdir}/transactional-update.conf -%{_mandir}/man5/transactional-update.conf.5%{ext_man} -%{_mandir}/man8/transactional-update.8%{ext_man} -%{_mandir}/man8/transactional-update.timer.8%{ext_man} -%{_mandir}/man8/transactional-update.service.8%{ext_man} +%dir %{_prefix}/lib/dracut +%dir %{_prefix}/lib/dracut/modules.d +%{_prefix}/lib/dracut/modules.d/50transactional-update/ +%{_mandir}/man5/transactional-update.conf.5%{?ext_man} +%{_mandir}/man8/transactional-update.8%{?ext_man} +%{_mandir}/man8/transactional-update.timer.8%{?ext_man} +%{_mandir}/man8/transactional-update.service.8%{?ext_man} %changelog ++++++ transactional-update-1.29.tar.bz2 -> transactional-update-2.0.tar.bz2 ++++++ ++++ 3510 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/Makefile.am new/transactional-update-2.0/Makefile.am --- old/transactional-update-1.29/Makefile.am 2018-01-22 13:01:43.000000000 +0100 +++ new/transactional-update-2.0/Makefile.am 2018-04-20 18:34:52.000000000 +0200 @@ -5,7 +5,7 @@ # AUTOMAKE_OPTIONS = 1.6 foreign check-news dist-bzip2 # -SUBDIRS = sbin man systemd logrotate doc etc +SUBDIRS = sbin man systemd logrotate dracut doc etc CLEANFILES = *~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/NEWS new/transactional-update-2.0/NEWS --- old/transactional-update-1.29/NEWS 2018-04-20 15:55:53.000000000 +0200 +++ new/transactional-update-2.0/NEWS 2018-04-23 10:03:28.000000000 +0200 @@ -2,6 +2,11 @@ Copyright (C) 2016, 2017, 2018 Thorsten Kukuk +Version 2.0 +* Create missing directories from rpm database during boot +* Merge /etc overlay with root subvolume during update +* Implement register option + Version 1.29 * Implement self-update * Disable optical media on dup diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/configure.ac new/transactional-update-2.0/configure.ac --- old/transactional-update-1.29/configure.ac 2018-04-20 15:55:00.000000000 +0200 +++ new/transactional-update-2.0/configure.ac 2018-04-23 09:57:43.000000000 +0200 @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(transactional-update, 1.29) +AC_INIT(transactional-update, 2.0) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([sbin/transactional-update.in]) AC_PREFIX_DEFAULT(/usr) @@ -16,25 +16,30 @@ ISSUEDIR=${prefix}/lib/issue.d UDEVRULESDIR=${prefix}/lib/udev/rules.d SYSTEMDDIR=${prefix}/lib/systemd/system + DRACUTDIR=${prefix}/lib/dracut/modules.d else TMPFILESDIR=${exec_prefix}/lib/tmpfiles.d ISSUEDIR=${exec_prefix}/lib/issue.d UDEVRULESDIR=${exec_prefix}/lib/udev/rules.d SYSTEMDDIR=${exec_prefix}/lib/systemd/system + DRACUTDIR=${exec_prefix}/lib/dracut/modules.d fi else TMPFILESDIR=${libexecdir}/tmpfiles.d ISSUEDIR=${libexecdir}/issue.d UDEVRULESDIR=${libexecdir}/udev/rules.d SYSTEMDDIR=${libexecdir}/systemd/system + DRACUTDIR=${libexecdir}/dracut/modules.d fi AC_SUBST(TMPFILESDIR) AC_SUBST(ISSUEDIR) AC_SUBST(UDEVRULESDIR) AC_SUBST(SYSTEMDDIR) +AC_SUBST(DRACUTDIR) LOGROTATEDDIR=${sysconfdir}/logrotate.d AC_SUBST(LOGROTATEDDIR) +AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL AC_PROG_LN_S @@ -47,6 +52,11 @@ PKG_CHECK_MODULES(LIBZYPP, libzypp) dnl +dnl Check for librpm +dnl +PKG_CHECK_MODULES([LIBRPM], [rpm]) + +dnl dnl Check for xsltproc dnl enable_man=yes @@ -73,5 +83,5 @@ fi AC_OUTPUT([Makefile sbin/Makefile man/Makefile systemd/Makefile \ - logrotate/Makefile doc/Makefile etc/Makefile \ + logrotate/Makefile dracut/Makefile doc/Makefile etc/Makefile \ sbin/transactional-update]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/dracut/Makefile.am new/transactional-update-2.0/dracut/Makefile.am --- old/transactional-update-1.29/dracut/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-2.0/dracut/Makefile.am 2018-04-20 18:34:52.000000000 +0200 @@ -0,0 +1,9 @@ +# +# Copyright (c) 2018 Ignaz Forster <ifors...@suse.de> +# + +modulesdir = @DRACUTDIR@/50transactional-update + +modules_SCRIPTS = transactional-update-etc-cleaner.sh transactional-update-etc-cleaner.service module-setup.sh + +EXTRA_DIST = $(SCRIPTS) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/dracut/module-setup.sh new/transactional-update-2.0/dracut/module-setup.sh --- old/transactional-update-1.29/dracut/module-setup.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-2.0/dracut/module-setup.sh 2018-04-20 18:34:52.000000000 +0200 @@ -0,0 +1,21 @@ +#!/bin/bash + +# called by dracut +check() { + test -f /etc/fstab.sys || [[ -n $add_fstab || -n $fstab_lines ]] +} + +# called by dracut +depends() { + echo fstab-sys +} + +# called by dracut +install() { + inst_script "$moddir/transactional-update-etc-cleaner.sh" /bin/transactional-update-etc-cleaner + inst_simple "$moddir/transactional-update-etc-cleaner.service" $systemdsystemunitdir/transactional-update-etc-cleaner.service + mkdir -p "${initdir}/$systemdsystemunitdir/initrd.target.wants" + ln_r "$systemdsystemunitdir/transactional-update-etc-cleaner.service" "$systemdsystemunitdir/initrd.target.wants/transactional-update-etc-cleaner.service" + inst_multiple stat rmdir + inst_multiple -o getfattr +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/dracut/transactional-update-etc-cleaner.service new/transactional-update-2.0/dracut/transactional-update-etc-cleaner.service --- old/transactional-update-1.29/dracut/transactional-update-etc-cleaner.service 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-2.0/dracut/transactional-update-etc-cleaner.service 2018-04-20 18:34:52.000000000 +0200 @@ -0,0 +1,16 @@ +[Unit] +Description=transactional-update /etc overlay cleaner +Requires=dracut-pre-pivot.service +After=dracut-pre-pivot.service +Before=initrd-cleanup.service +DefaultDependencies=no +ConditionPathExists=/sysroot/var/lib/overlay/etc/transactional-update.newsnapshot + +[Service] +Type=oneshot +Environment=DRACUT_SYSTEMD=1 +Environment=NEWROOT=/sysroot +StandardInput=null +StandardOutput=syslog +StandardError=syslog+console +ExecStart=/bin/transactional-update-etc-cleaner diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/dracut/transactional-update-etc-cleaner.sh new/transactional-update-2.0/dracut/transactional-update-etc-cleaner.sh --- old/transactional-update-1.29/dracut/transactional-update-etc-cleaner.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-2.0/dracut/transactional-update-etc-cleaner.sh 2018-04-20 18:34:52.000000000 +0200 @@ -0,0 +1,133 @@ +#!/bin/bash -e +# +# Purge contents of etc overlay on first boot after creating new snapshot +# +# Author: Ignaz Forster <ifors...@suse.de> +# Copyright (C) 2018 SUSE Linux GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +ETC_OVERLAY="${NEWROOT}/var/lib/overlay/etc" +TU_FLAGFILE="${ETC_OVERLAY}/transactional-update.newsnapshot" + +# Import common dracut variables +. /dracut-state.sh 2>/dev/null + +same_file() { + # Primary indicators for changes: File size, attributes and modification time + local overlay_stat="`stat --format="%a %u:%g %s %t/%T %F %Y" -- "$1"`" + local lowerdir_stat="`stat --format="%a %u:%g %s %t/%T %F %Y" -- "$2"`" + if [ "${overlay_stat}" != "${lowerdir_stat}" ]; then + return 1 + fi + + # Compare extended attributes if available + if [ -x /usr/bin/getfattr ]; then + overlay_stat="`getfattr --dump --no-dereference --absolute-names -- "$1" | sed 1d`" + lowerdir_stat="`getfattr --dump --no-dereference --absolute-names -- "$2" | sed 1d`" + if [ "${overlay_stat}" != "${lowerdir_stat}" ]; then + return 1 + fi + fi + + # Files seem to be identical + return 0 +} + +# Remove files from overlay with safety checks +# etc directory mustn't be deleted completely, as it's still mounted and the kernel won't be able to recover from that (different inode?) +clean_overlay() { + local dir="${1:-.}" + local file + local snapdir="${NEWROOT}/${PREV_SNAPSHOT_DIR}/etc/${dir}" + + pushd "${ETC_OVERLAY}/${dir}" >/dev/null + for file in .[^.]* ..?* *; do + # Filter unexpanded globs of "for" loop + if [ ! -e "${file}" ]; then + continue + fi + + # Recursively process directories + if [ -d "${file}" ]; then + clean_overlay "${dir}/${file}" + if same_file "${file}" "${snapdir}/${file}"; then + rmdir --ignore-fail-on-non-empty -- "${file}" + fi + # Overlayfs creates a character device with device number 0/0 for removed files / directories + elif [ -c "${file}" -a "`stat --format="%t/%T" -- "${file}"`" = "0/0" -a ! -e "${snapdir}/${file}" ]; then + echo "Removing character device ${dir}/${file} from overlay..." + rm -- "${file}" + # Verify that files in the overlay haven't changed since taking the snapshot + elif same_file "${file}" "${snapdir}/${file}"; then + echo "Removing copy of ${dir}/${file} from overlay..." + rm -- "${file}" + # File seems to have been modified, warn user + else + echo "Warning: Not removing ${dir}/${file} - modified after snapshot creation." + fi + done + popd >/dev/null +} + +# Delete all contents of the overlay +remove_overlay() { + echo "Previous snapshot ${PREV_SNAPSHOT_ID} not available; deleting all overlay contents." + cd "${ETC_OVERLAY}" + rm -rf -- .[^.]* ..?* * +} + +# Mount directories necessary for file comparison +# Note: Will not break execution if snapshot is not available +prepare_environment() { + if ! findmnt "${NEWROOT}/.snapshots" >/dev/null; then + if ! mount -t btrfs -o ro,subvol=@/.snapshots ${root#block:*} ${NEWROOT}/.snapshots; then + echo "Could not mount .snapshots!" + return 1 + fi + UMOUNT_DOT_SNAPSHOTS=1 + fi + CURRENT_SNAPSHOT_ID="`btrfs subvolume get-default /${NEWROOT} | sed 's#.*/.snapshots/\(.*\)/snapshot#\1#g'`" + PREV_SNAPSHOT_ID="`sed -n 's#.*<pre_num>\([^>]\+\)</pre_num>#\1#p' ${NEWROOT}/.snapshots/${CURRENT_SNAPSHOT_ID}/info.xml`" + PREV_SNAPSHOT_DIR="`btrfs subvolume list /${NEWROOT} | sed -n 's#.*\(/.snapshots/'${PREV_SNAPSHOT_ID}'/snapshot\)#\1#p'`" + if [ -n "${PREV_SNAPSHOT_DIR}" ] && ! findmnt "${NEWROOT}/${PREV_SNAPSHOT_DIR}" >/dev/null; then + if ! mount -t btrfs -o ro,subvol=@${PREV_SNAPSHOT_DIR} ${root#block:*} "${NEWROOT}/${PREV_SNAPSHOT_DIR}"; then + echo "Warning: Could not mount old snapshot directory ${PREV_SNAPSHOT_DIR}!" + return 1 + fi + UMOUNT_PREV_SNAPSHOT=1 + fi + return 0 +} + +release_environment() { + if [ -n "${UMOUNT_PREV_SNAPSHOT}" ]; then + umount "${NEWROOT}/${PREV_SNAPSHOT_DIR}" + fi + if [ -n "${UMOUNT_DOT_SNAPSHOTS}" ]; then + umount "${NEWROOT}/.snapshots" + fi +} + +if [ -e "${ETC_OVERLAY}" -a -e "${TU_FLAGFILE}" ]; then + # Previous snapshot may not be available; just delete all overlay contents in this case + rm "${TU_FLAGFILE}" + if prepare_environment; then + clean_overlay + else + remove_overlay + fi + mount -o remount "${NEWROOT}/etc" + release_environment +fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/sbin/Makefile.am new/transactional-update-2.0/sbin/Makefile.am --- old/transactional-update-1.29/sbin/Makefile.am 2018-04-20 15:48:00.000000000 +0200 +++ new/transactional-update-2.0/sbin/Makefile.am 2018-04-20 18:34:52.000000000 +0200 @@ -3,9 +3,11 @@ # sbin_SCRIPTS = transactional-update tu-rebuild-kdump-initrd -sbin_PROGRAMS = transactional-update-helper +sbin_PROGRAMS = transactional-update-helper create_dirs_from_rpmdb transactional_update_helper_SOURCES = transactional-update-helper.cpp transactional_update_helper_LDADD = @LIBZYPP_LIBS@ +create_dirs_from_rpmdb_SOURCES = create_dirs_from_rpmdb.c +create_dirs_from_rpmdb_LDADD = @LIBRPM_LIBS@ CLEANFILES = transactional-update EXTRA_DIST = transactional-update.in tu-rebuild-kdump-initrd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/sbin/create_dirs_from_rpmdb.c new/transactional-update-2.0/sbin/create_dirs_from_rpmdb.c --- old/transactional-update-1.29/sbin/create_dirs_from_rpmdb.c 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-2.0/sbin/create_dirs_from_rpmdb.c 2018-04-24 14:06:06.000000000 +0200 @@ -0,0 +1,300 @@ +/* create_dirs_from_rpmdb - Create missing directories in /srv,/var during boot + + Copyright (C) 2018 SUSE Linux GmbH + Author: Thorsten Kukuk <ku...@suse.de> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <time.h> +#include <unistd.h> +#include <getopt.h> +#include <rpm/rpmcli.h> +#include <rpm/rpmts.h> +#include <rpm/rpmdb.h> + +static int debug_flag = 0; +static int verbose_flag = 0; + +/* Print the version information. */ +static void +print_version (void) +{ + fprintf (stdout, "create_dirs_from_rpmdb (%s) %s\n", PACKAGE, VERSION); +} + +static void +print_usage (FILE *stream) +{ + fprintf (stream, "Usage: create_dirs_from_rpmdb [-V|--version] [--debug] [-v|--verbose]\n"); +} + +static void +print_error (void) +{ + fprintf (stderr, + "Try `create_dirs_from_rpmdb --help' or `create_dirs_from_rpmdb --usage' for more information.\n"); +} + + +static char * +fmode2str (int mode) +{ + char *str = strdup("----------"); + + if (str == NULL) + { + fprintf (stderr, "Out of memory\n"); + exit (1); + } + + if (S_ISREG(mode)) + str[0] = '-'; + else if (S_ISDIR(mode)) + str[0] = 'd'; + else if (S_ISCHR(mode)) + str[0] = 'c'; + else if (S_ISBLK(mode)) + str[0] = 'b'; + else if (S_ISFIFO(mode)) + str[0] = 'p'; + else if (S_ISLNK(mode)) + str[0] = 'l'; + else if (S_ISSOCK(mode)) + str[0] = 's'; + else + str[0] = '?'; + + if (mode & S_IRUSR) str[1] = 'r'; + if (mode & S_IWUSR) str[2] = 'w'; + if (mode & S_IXUSR) str[3] = 'x'; + + if (mode & S_IRGRP) str[4] = 'r'; + if (mode & S_IWGRP) str[5] = 'w'; + if (mode & S_IXGRP) str[6] = 'x'; + + if (mode & S_IROTH) str[7] = 'r'; + if (mode & S_IWOTH) str[8] = 'w'; + if (mode & S_IXOTH) str[9] = 'x'; + + if (mode & S_ISUID) + str[3] = ((mode & S_IXUSR) ? 's' : 'S'); + + if (mode & S_ISGID) + str[6] = ((mode & S_IXGRP) ? 's' : 'S'); + + if (mode & S_ISVTX) + str[9] = ((mode & S_IXOTH) ? 't' : 'T'); + + return str; +} + +int +check_package (rpmts ts, Header h) +{ + int ec = 0; + rpmfi fi = NULL; + rpmfiFlags fiflags = (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY); + + fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags); + if (rpmfiFC(fi) <= 0) + goto exit; + + fi = rpmfiInit (fi, 0); + while (rpmfiNext (fi) >= 0) + { + rpm_mode_t fmode = rpmfiFMode(fi); + + if (S_ISDIR(fmode)) + { + const char *prefixes[] = {"/var/", "/srv/"}; + const char *fn = rpmfiFN(fi); + rpmfileAttrs fflags = rpmfiFFlags(fi); + int i; + + for (i = 0; i < sizeof (prefixes)/sizeof(char *); i++) + { + if (!(fflags & RPMFILE_GHOST) && + strncmp (prefixes[i], fn, strlen (prefixes[i]))== 0 && + access (fn, F_OK) == -1) + { + int rc = 0; + struct tm * tm; + char timefield[100]; + rpm_time_t fmtime = rpmfiFMtime(fi); + time_t mtime = fmtime; /* important if sizeof(int32_t) ! sizeof(time_t) */ + const char *fuser = rpmfiFUser(fi); + const char *fgroup = rpmfiFGroup(fi); + uid_t user_id; + gid_t group_id; + struct passwd *pwd; + struct group *grp; + struct timeval stamps[2] = { + { .tv_sec = mtime, .tv_usec = 0 }, + { .tv_sec = mtime, .tv_usec = 0 }}; + + + if (debug_flag) + { + char *perms = fmode2str (fmode); + + /* Convert file mtime to display format */ + tm = localtime(&mtime); + timefield[0] = '\0'; + if (tm != NULL) + { + const char *fmt = "%F,%H:%M"; + (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm); + } + + printf ("Create %s (%s,%s,%s,%s)\n", fn, perms, fuser, + fgroup, timefield); + free (perms); + } + else if (verbose_flag) + printf ("Create %s\n", fn); + + rc = mkdir (fn, fmode); + if (rc < 0) + { + fprintf (stderr, "Failed to create directory '%s': %m\n", fn); + ec = 1; + goto exit; + } + + pwd = getpwnam (fuser); + grp = getgrnam (fgroup); + + if (pwd == NULL || grp == NULL) + { + fprintf (stderr, "Failed to resolve %s/%s\n", fuser, fgroup); + rmdir (fn); + ec = 1; + goto exit; + } + + user_id = pwd->pw_uid; + group_id = grp->gr_gid; + + rc = chown (fn, user_id, group_id); + if (rc < 0) + { + fprintf (stderr, "Failed to set owner/group for '%s': %m\n", fn); + /* wrong permissions are bad, remove dir and continue */ + rmdir (fn); + ec = 1; + goto exit; + } + /* ignore errors here, time stamps are not critical */ + utimes (fn, stamps); + } + } + } + } + + exit: + rpmfiFree(fi); + + return ec; +} + +int +main (int argc, char *argv[]) +{ + Header h; + rpmts ts = NULL; + int ec = 0; + + + while (1) + { + int c; + int option_index = 0; + static struct option long_options[] = { + {"version", no_argument, NULL, 'V' }, + {"usage", no_argument, NULL, 'u' }, + {"debug", no_argument, NULL, 254 }, + {"verbose", no_argument, NULL, 'v' }, + {"help", no_argument, NULL, 255 }, + {NULL, 0, NULL, 0 } + }; + + /* Don't let getopt print error messages, we do it ourself. */ + opterr = 0; + + c = getopt_long (argc, argv, "uVv", + long_options, &option_index); + + if (c == (-1)) + break; + + switch (c) + { + case 'V': + print_version (); + return 0; + case 255: + case 'u': + print_usage (stdout); + return 0; + case 'v': + verbose_flag = 1; + break; + case 254: + debug_flag = 1; + break; + default: + break; + } + } + + argc -= optind; + argv += optind; + + if (argc > 0) + { + fprintf (stderr, "create_dirs_from_rpmdb: Too many arguments.\n"); + print_error (); + return 1; + } + + rpmReadConfigFiles (NULL, NULL); + + ts = rpmtsCreate (); + rpmtsSetRootDir (ts, rpmcliRootDir); + + rpmdbMatchIterator mi = rpmtsInitIterator (ts, RPMDBI_PACKAGES, NULL, 0); + if (mi == NULL) + return 1; + + while ((h = rpmdbNextIterator (mi)) != NULL) + { + int rc; + /* rpmsqPoll (); */ + if ((rc = check_package (ts, h)) != 0) + ec = rc; + } + + rpmdbFreeIterator (mi); + rpmtsFree (ts); + + return ec; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/sbin/transactional-update-helper.cpp new/transactional-update-2.0/sbin/transactional-update-helper.cpp --- old/transactional-update-1.29/sbin/transactional-update-helper.cpp 2018-04-20 11:09:40.000000000 +0200 +++ new/transactional-update-2.0/sbin/transactional-update-helper.cpp 2018-04-20 18:34:52.000000000 +0200 @@ -1,5 +1,6 @@ /* transactional-update-helper - native helper scripts for transactional-update + Author: Ignaz Forster <ifors...@suse.de> Copyright (C) 2018 SUSE Linux GmbH This program is free software: you can redistribute it and/or modify diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/sbin/transactional-update.in new/transactional-update-2.0/sbin/transactional-update.in --- old/transactional-update-1.29/sbin/transactional-update.in 2018-04-20 15:47:38.000000000 +0200 +++ new/transactional-update-2.0/sbin/transactional-update.in 2018-04-20 20:40:09.000000000 +0200 @@ -34,6 +34,8 @@ DO_DUP=0 DO_ROLLBACK=0 DO_SELF_UPDATE=1 +DO_REGISTRATION=0 +REGISTRATION_ARGS="" ROLLBACK_SNAPSHOT=0 REBOOT_AFTERWARDS=0 REBOOT_METHOD="auto" @@ -43,17 +45,14 @@ SYSTEMCONFFILE="@prefix@@sysconfdir@/transactional-update.conf" LOGFILE="/var/log/transactional-update.log" STATE_FILE="/var/lib/misc/transactional-update.state" +NEW_SNAPSHOT_FLAG="/var/lib/overlay/etc/transactional-update.newsnapshot" SELF_PATH="`dirname $0`" PACKAGE_UPDATES=0 ZYPPER_AUTO_IMPORT_KEYS=0 HAS_SEPARATE_VAR=0 SNAPSHOT_ID="" -SECOND_SNAPSHOT_ID="" +BACKUP_SNAPSHOT_ID="" SNAPPER_NO_DBUS="" -KDUMP_SYSCONFIG="/etc/sysconfig/kdump" -# Config files which the user could have modified and which should -# be copied to the snapshot. -CONFIG_FILES_TO_COPY="/etc/default/grub" # Load config if [ -r ${SYSTEMCONFFILE} ]; then @@ -85,6 +84,7 @@ echo " transactional-update [--no-selfupdate] [cleanup][up|dup|patch|initrd][kdump][reboot]" echo " transactional-update [--no-selfupdate] [cleanup] [reboot] pkg install|remove|update PKG1..PKGN" echo " transactional-update [--no-selfupdate] migration" + echo " transactional-update [--no-selfupdate] register -p <product> [-r <registration code>]" echo " transactional-update rollback [number]" exit $1 } @@ -152,51 +152,19 @@ test -f /usr/lib/systemd/system/kdump.service || return systemctl is-enabled --quiet kdump.service if [ $? = 0 -a -x ${MOUNT_DIR}/usr/sbin/tu-rebuild-kdump-initrd ]; then - if [ ${KDUMP_SYSCONFIG} -nt ${MOUNT_DIR}/${KDUMP_SYSCONFIG} ]; then - cp -a ${KDUMP_SYSCONFIG} ${MOUNT_DIR}/${KDUMP_SYSCONFIG} - fi chroot ${MOUNT_DIR} /usr/sbin/tu-rebuild-kdump-initrd |& tee -a ${LOGFILE} fi } -# If the SHA256 sum of passwd, group or shadow has changed then copy the files -# to /usr/etc. If libnss_usrfiles is installed those files will be taken into -# account as a secondary source if an entry is not found in /etc. -calc_user_group_sha256sum () { - SHA256_passwd=`sha256sum ${MOUNT_DIR}/etc/passwd | awk '{ print $1 }'` - SHA256_group=`sha256sum ${MOUNT_DIR}/etc/group | awk '{ print $1 }'` - SHA256_shadow=`sha256sum ${MOUNT_DIR}/etc/shadow | awk '{ print $1 }'` -} - -copy_user_group_accounts () { - # Only copy files to usr/etc if libnss_usrfiles is installed. - test -d ${MOUNT_DIR}/usr/etc || return - - NEWSUM=`sha256sum ${MOUNT_DIR}/etc/passwd | awk '{ print $1 }'` - if [ ${NEWSUM} != ${SHA256_passwd} ]; then - cp -a ${MOUNT_DIR}/etc/passwd ${MOUNT_DIR}/usr/etc/ - fi - - NEWSUM=`sha256sum ${MOUNT_DIR}/etc/group | awk '{ print $1 }'` - if [ ${NEWSUM} != ${SHA256_group} ]; then - cp -a ${MOUNT_DIR}/etc/group ${MOUNT_DIR}/usr/etc/ - fi - - NEWSUM=`sha256sum ${MOUNT_DIR}/etc/shadow | awk '{ print $1 }'` - if [ ${NEWSUM} != ${SHA256_shadow} ]; then - cp -a ${MOUNT_DIR}/etc/shadow ${MOUNT_DIR}/usr/etc/ - fi -} - # Only called in error case; reverts everything to previous state. quit() { if [ -n "${SNAPSHOT_ID}" ] ; then log_error "Removing snapshot #${SNAPSHOT_ID}..." snapper ${SNAPPER_NO_DBUS} delete ${SNAPSHOT_ID} |& tee -a ${LOGFILE} fi - if [ -n "${SECOND_SNAPSHOT_ID}" ] ; then - log_error "Removing snapshot #${SECOND_SNAPSHOT_ID}..." - snapper ${SNAPPER_NO_DBUS} delete ${SECOND_SNAPSHOT_ID} |& tee -a ${LOGFILE} + if [ -n "${BACKUP_SNAPSHOT_ID}" ] ; then + log_error "Removing snapshot #${BACKUP_SNAPSHOT_ID}..." + snapper ${SNAPPER_NO_DBUS} delete ${BACKUP_SNAPSHOT_ID} |& tee -a ${LOGFILE} fi if [ $USE_SALT_GRAINS -eq 1 ]; then if [ -f /etc/salt/grains ]; then @@ -406,6 +374,24 @@ DO_SELF_UPDATE=0 shift ;; + register) + DO_REGISTRATION=1 + shift + + # Collect arguments for Registration + if [ $# -eq 0 ]; then + usage 1 + fi + + while [ 1 ]; do + if [ $# -eq 0 ]; then + break; + else + REGISTRATION_ARGS="${REGISTRATION_ARGS} $1"; + shift + fi + done + ;; -h|--help) usage 0 ;; @@ -581,10 +567,13 @@ if [ -n "${ZYPPER_ARG}" -o ${REWRITE_GRUB_CFG} -eq 1 \ -o ${REWRITE_INITRD} -eq 1 -o ${REBUILD_KDUMP_INITRD} -eq 1 \ - -o ${RUN_SHELL} -eq 1 -o ${REWRITE_BOOTLOADER} -eq 1 ]; then + -o ${RUN_SHELL} -eq 1 -o ${REWRITE_BOOTLOADER} -eq 1 \ + -o ${DO_REGISTRATION} -eq 1 ]; then - # Check if there are updates at all. - if [ -n "${ZYPPER_ARG}" -a -n "${ZYPPER_NONINTERACTIVE}" ]; then + # Check if there are updates at all. Don't check if we do + # a registration, as this can change the zypper result. + if [ -n "${ZYPPER_ARG}" -a -n "${ZYPPER_NONINTERACTIVE}" \ + -a ${DO_REGISTRATION} -eq 0 ]; then if [ $DO_DUP -eq 1 ]; then "${SELF_PATH}"/transactional-update-helper disable-optical fi @@ -616,25 +605,41 @@ fi fi - # If the current root file system is not read-only, a read-only copy for rollback has to be created first. + # Create a backup snapshot of the current system state (i.e. preserve the /etc overlay contents on a read-only + # system or the general system state on a read-write file system, similar to what zypper / snapper does). # Hint: The rw subvolume is not shown in grub2. - if [ ${RO_ROOT} == "false" ]; then - log_info "Creating read-only snapshot of current read-write root filesystem (#${CURRENT_SNAPSHOT_ID})" - SECOND_SNAPSHOT_ID=`snapper create -p -c number -u "important=yes" -d "RO-Clone of #${CURRENT_SNAPSHOT_ID}"` + log_info "Creating read-only snapshot of current system state (#${CURRENT_SNAPSHOT_ID})" + BACKUP_SNAPSHOT_ID=`snapper create -p -t pre -c number -u "important=yes" -d "RO-Clone of #${CURRENT_SNAPSHOT_ID}"` + BACKUP_SNAPSHOT_DIR=/.snapshots/${BACKUP_SNAPSHOT_ID}/snapshot + if [ $? -ne 0 ]; then + SNAPPER_NO_DBUS="--no-dbus" + BACKUP_SNAPSHOT_ID=`snapper --no-dbus create -p -t pre -c number -u "important=yes" -d "RO-Clone of #${CURRENT_SNAPSHOT_ID}"` if [ $? -ne 0 ]; then - SNAPPER_NO_DBUS="--no-dbus" - SECOND_SNAPSHOT_ID=`snapper --no-dbus create -p -c number -u "important=yes" -d "RO-Clone of #${CURRENT_SNAPSHOT_ID}"` - if [ $? -ne 0 ]; then - log_error "ERROR: snapper create failed!" - exit 1 - fi + log_error "ERROR: snapper create failed!" + exit 1 fi fi - SNAPSHOT_ID=`snapper create -p -u "transactional-update-in-progress=yes" -d "Snapshot Update"` + # Make the backup snapshot read-write: + btrfs property set ${BACKUP_SNAPSHOT_DIR} ro false + if [ $? -ne 0 ]; then + log_error "ERROR: changing ${BACKUP_SNAPSHOT_DIR} to read-write failed!" + quit 1; + fi + + # Copy the contents of /etc into the backup snapshot + log_info "Copying /etc state into backup snapshot" + rsync --archive --xattrs --acls --quiet --exclude "${NEW_SNAPSHOT_FLAG}" --delete-excluded /etc ${BACKUP_SNAPSHOT_DIR} + if [ $? -ne 0 ]; then + log_error "ERROR: copying of /etc into backup snapshot failed!" + quit 1; + fi + + # Create the working snapshot + SNAPSHOT_ID=`snapper create --type post --pre-number ${BACKUP_SNAPSHOT_ID} -p -u "transactional-update-in-progress=yes" -d "Snapshot Update"` if [ $? -ne 0 ]; then SNAPPER_NO_DBUS="--no-dbus" - SNAPSHOT_ID=`snapper --no-dbus create -p -u "transactional-update-in-progress=yes" -d "Snapshot Update"` + SNAPSHOT_ID=`snapper --no-dbus create --type post --pre-number ${BACKUP_SNAPSHOT_ID} -p -u "transactional-update-in-progress=yes" -d "Snapshot Update"` if [ $? -ne 0 ]; then log_error "ERROR: snapper create failed!" quit 1 @@ -675,11 +680,7 @@ rm -f ${SNAPSHOT_DIR}/var/update_snapshot.test fi - # On a read only system, make sure that /etc/zypp in the - # snapshot is current, could come from a overlayfs which - # means not part of the snapshot itself if [ ${RO_ROOT} == "true" ]; then - DIR_TO_MOUNT="${DIR_TO_MOUNT} etc/zypp" if [ ${RUN_SHELL} -eq 1 ]; then DIR_TO_MOUNT="${DIR_TO_MOUNT} root" fi @@ -722,6 +723,14 @@ fi done + # Copy the contents of /etc + log_info "Copying /etc state into snapshot" + rsync --archive --xattrs --acls --quiet --exclude "${NEW_SNAPSHOT_FLAG}" --delete-excluded /etc ${SNAPSHOT_DIR} + if [ $? -ne 0 ]; then + log_error "ERROR: copying of /etc into snapshot failed!" + quit 1; + fi + # If we have a seperate /var, create some directories which we # will delete again later. if [ ${HAS_SEPARATE_VAR} -eq 1 ]; then @@ -752,17 +761,12 @@ # transactional update export TRANSACTIONAL_UPDATE=true - # Copy modified config files to the snapshot - for CF in ${CONFIG_FILES_TO_COPY} ; do - if [ ${CF} -nt ${MOUNT_DIR}/${CF} ]; then - cp -a ${CF} ${MOUNT_DIR}/${CF} - fi - done + if [ ${DO_REGISTRATION} -eq 1 ]; then + SUSEConnect --root ${MOUNT_DIR} ${REGISTRATION_ARGS} + fi if [ -n "${ZYPPER_ARG}" ]; then - calc_user_group_sha256sum - log_info "Calling zypper ${ZYPPER_ARG}" if [ -n "${ZYPPER_NONINTERACTIVE}" ]; then env DISABLE_RESTART_ON_UPDATE=yes zypper -R ${MOUNT_DIR} ${ZYPPER_ARG} ${ZYPPER_NONINTERACTIVE} ${ZYPPER_ARG_PKGS} |& tee -a ${LOGFILE} @@ -786,7 +790,6 @@ fi if [ $RETVAL -eq 0 -o $RETVAL -eq 102 -o $RETVAL -eq 103 -o \( $DO_DUP -eq 0 -a $RETVAL -eq 106 \) ]; then - copy_user_group_accounts REBUILD_KDUMP_INITRD=1 # check if products are updated and we need to re-register # at next boot. @@ -893,6 +896,8 @@ # Save the old snapshot or else it will get lost. add_unique_id ${CURRENT_SNAPSHOT_ID} save_state_file ${SNAPSHOT_ID} + # Create flag file for overlay purging + touch "${NEW_SNAPSHOT_FLAG}" # Reset in-progress flag snapper ${SNAPPER_NO_DBUS} modify -u "transactional-update-in-progress=" ${SNAPSHOT_ID} fi @@ -905,6 +910,13 @@ log_error "ERROR: changing ${SNAPSHOT_DIR} to ro=${RO_ROOT} failed!" EXITCODE=1 fi + if [ -n ${BACKUP_SNAPSHOT_DIR} ]; then + btrfs property set ${BACKUP_SNAPSHOT_DIR} ro ${RO_ROOT} + if [ $? -ne 0 ]; then + log_error "ERROR: changing ${BACKUP_SNAPSHOT_DIR} to ro=${RO_ROOT} failed!" + EXITCODE=1 + fi + fi if [ ${EXITCODE} -ne 0 ]; then quit ${EXITCODE} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/systemd/Makefile.am new/transactional-update-2.0/systemd/Makefile.am --- old/transactional-update-1.29/systemd/Makefile.am 2018-04-20 15:48:00.000000000 +0200 +++ new/transactional-update-2.0/systemd/Makefile.am 2018-04-20 18:34:52.000000000 +0200 @@ -4,6 +4,7 @@ systemddir = @SYSTEMDDIR@ -systemd_DATA = transactional-update.timer transactional-update.service +systemd_DATA = transactional-update.timer transactional-update.service \ + create-dirs-from-rpmdb.service EXTRA_DIST = $(DATA) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/transactional-update-1.29/systemd/create-dirs-from-rpmdb.service new/transactional-update-2.0/systemd/create-dirs-from-rpmdb.service --- old/transactional-update-1.29/systemd/create-dirs-from-rpmdb.service 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-2.0/systemd/create-dirs-from-rpmdb.service 2018-04-24 13:38:42.000000000 +0200 @@ -0,0 +1,12 @@ +[Unit] +Description=Create missing directories from rpmdb +Documentation=man:create_dirs_from_rpmdb(8) +After=local-fs.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/sbin/create_dirs_from_rpmdb -v + +[Install] +WantedBy=multi-user.target