Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package yast2-installation for 
openSUSE:Factory checked in at 2021-08-28 22:28:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yast2-installation (Old)
 and      /work/SRC/openSUSE:Factory/.yast2-installation.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yast2-installation"

Sat Aug 28 22:28:50 2021 rev:477 rq:914406 version:4.4.17

Changes:
--------
--- /work/SRC/openSUSE:Factory/yast2-installation/yast2-installation.changes    
2021-08-19 12:49:14.337451964 +0200
+++ 
/work/SRC/openSUSE:Factory/.yast2-installation.new.1899/yast2-installation.changes
  2021-08-28 22:28:57.617964891 +0200
@@ -1,0 +2,13 @@
+Tue Aug 24 14:58:48 UTC 2021 - Stefan Hundhammer <shundham...@suse.com>
+
+- Refactored umount_finish.rb (bsc#1149980)
+  More details: https://github.com/yast/yast-installation/pull/975
+  - Moved out the unmounting part to a new (testable!) Unmounter class
+  - Now using a dedicated FinishClient base class
+  - Killed a lot of YCP zombies
+  - Removed dead code going back to storage-old
+  - Modularized the code
+  - Made the client invokable stand-alone
+- 4.4.17
+
+-------------------------------------------------------------------

Old:
----
  yast2-installation-4.4.16.tar.bz2

New:
----
  yast2-installation-4.4.17.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ yast2-installation.spec ++++++
--- /var/tmp/diff_new_pack.cGVOn8/_old  2021-08-28 22:28:58.229965526 +0200
+++ /var/tmp/diff_new_pack.cGVOn8/_new  2021-08-28 22:28:58.229965526 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-installation
-Version:        4.4.16
+Version:        4.4.17
 Release:        0
 Summary:        YaST2 - Installation Parts
 License:        GPL-2.0-only

++++++ yast2-installation-4.4.16.tar.bz2 -> yast2-installation-4.4.17.tar.bz2 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/package/yast2-installation.changes 
new/yast2-installation-4.4.17/package/yast2-installation.changes
--- old/yast2-installation-4.4.16/package/yast2-installation.changes    
2021-08-17 20:51:42.000000000 +0200
+++ new/yast2-installation-4.4.17/package/yast2-installation.changes    
2021-08-26 15:46:35.000000000 +0200
@@ -1,4 +1,17 @@
 -------------------------------------------------------------------
+Tue Aug 24 14:58:48 UTC 2021 - Stefan Hundhammer <shundham...@suse.com>
+
+- Refactored umount_finish.rb (bsc#1149980)
+  More details: https://github.com/yast/yast-installation/pull/975
+  - Moved out the unmounting part to a new (testable!) Unmounter class
+  - Now using a dedicated FinishClient base class
+  - Killed a lot of YCP zombies
+  - Removed dead code going back to storage-old
+  - Modularized the code
+  - Made the client invokable stand-alone
+- 4.4.17
+
+-------------------------------------------------------------------
 Wed Aug 11 20:43:04 UTC 2021 - Dirk M??ller <dmuel...@suse.com>
 
 - only list specific files installed in common directories (metainfo,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/package/yast2-installation.spec 
new/yast2-installation-4.4.17/package/yast2-installation.spec
--- old/yast2-installation-4.4.16/package/yast2-installation.spec       
2021-08-17 20:51:42.000000000 +0200
+++ new/yast2-installation-4.4.17/package/yast2-installation.spec       
2021-08-26 15:46:35.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-installation
-Version:        4.4.16
+Version:        4.4.17
 Release:        0
 Summary:        YaST2 - Installation Parts
 License:        GPL-2.0-only
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/src/clients/umount_finish.rb 
new/yast2-installation-4.4.17/src/clients/umount_finish.rb
--- old/yast2-installation-4.4.16/src/clients/umount_finish.rb  2021-08-17 
20:51:42.000000000 +0200
+++ new/yast2-installation-4.4.17/src/clients/umount_finish.rb  2021-08-26 
15:46:35.000000000 +0200
@@ -1,2 +1,4 @@
+require "yast"
 require "installation/clients/umount_finish"
-Yast::UmountFinishClient.new.main
+
+Installation::Clients::UmountFinishClient.run
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/src/lib/installation/clients/umount_finish.rb 
new/yast2-installation-4.4.17/src/lib/installation/clients/umount_finish.rb
--- old/yast2-installation-4.4.16/src/lib/installation/clients/umount_finish.rb 
2021-08-17 20:51:42.000000000 +0200
+++ new/yast2-installation-4.4.17/src/lib/installation/clients/umount_finish.rb 
2021-08-26 15:46:35.000000000 +0200
@@ -1,8 +1,7 @@
 # encoding: utf-8
-
 # 
------------------------------------------------------------------------------
 # Copyright (c) 2006-2012 Novell, Inc. All Rights Reserved.
-#
+# Copyright (c) 2013-2021 SUSE LLC
 #
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of version 2 of the GNU General Public License as published by the
@@ -11,99 +10,104 @@
 # 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, contact Novell, Inc.
-#
-# To contact Novell about this file by physical or electronic mail, you may 
find
-# current contact information at www.novell.com.
 # 
------------------------------------------------------------------------------
 
-# File:
-#  umount_finish.rb
-#
-# Module:
-#  Step of base installation finish
-#
-# Authors:
-#  Jiri Srain <jsr...@suse.cz>
-
+require "yast"
+require "installation/finish_client"
+require "installation/unmounter"
 require "y2storage"
-require "pathname"
-require "shellwords"
 
-module Yast
-  class UmountFinishClient < Client
-    include Yast::Logger
-
-    EFIVARS_PATH = "/sys/firmware/efi/efivars".freeze
-    USB_PATH = "/proc/bus/usb".freeze
-
-    def main
-      textdomain "installation"
-
-      Yast.import "Installation"
-      Yast.import "Hotplug"
-      Yast.import "String"
-      Yast.import "FileUtils"
-
-      @ret = nil
-      @func = ""
-      @param = {}
-
-      # Check arguments
-      if Ops.greater_than(Builtins.size(WFM.Args), 0) &&
-          Ops.is_string?(WFM.Args(0))
-        @func = Convert.to_string(WFM.Args(0))
-        if Ops.greater_than(Builtins.size(WFM.Args), 1) &&
-            Ops.is_map?(WFM.Args(1))
-          @param = Convert.to_map(WFM.Args(1))
-        end
+module Installation
+  module Clients
+    # Finish client to unmount all mounts to the target
+    class UmountFinishClient < FinishClient
+      include Yast::Logger
+
+      # Constructor
+      def initialize
+        textdomain "installation"
+        Yast.import "FileUtils"
+        @running_standalone = false
       end
 
-      Builtins.y2milestone("starting umount_finish")
-      Builtins.y2debug("func=%1", @func)
-      Builtins.y2debug("param=%1", @param)
-
-      if @func == "Info"
-        return {
-          "steps" => 1,
-          # progress step title
-          "title" => _(
-            "Unmounting all mounted devices..."
-          ),
-          "when"  => [:installation, :live_installation, :update, :autoinst]
-        }
-      elsif @func == "Write"
-
-        #
-        # !!! NO WRITE OPERATIONS TO THE TARGET HERE !!!
-        # !!! Use pre_umount_finish instead          !!!
-        #
-
-        Builtins.y2milestone(
-          "/proc/mounts:\n%1",
-          WFM.Read(path(".local.string"), "/proc/mounts")
-        )
-        Builtins.y2milestone(
-          "/proc/partitions:\n%1",
-          WFM.Read(path(".local.string"), "/proc/partitions")
-        )
+      # This can be used when invoking this file directly with
+      #   ruby ./umount_finish.rb
+      #
+      def run_standalone
+        @running_standalone = true
+        Installation.destdir = "/mnt" if Installation.destdir == "/"
+        write
+      end
 
-        # get mounts at and in the target from /proc/mounts - do not use
-        # Storage here since Storage does not know whether other processes,
-        # e.g. snapper, mounted filesystems in the target
-
-        umount_list = []
-        SCR.Read(path(".proc.mounts")).each do |entry|
-          mountpoint = entry["file"]
-          if mountpoint.start_with?(Installation.destdir)
-            umount_list << mountpoint[Installation.destdir.length, 
mountpoint.length]
-          end
+    protected
+
+      def title
+        # progress step title
+        _("Unmounting all mounted devices...")
+      end
+
+      def modes
+        # FIXME: better use 'nil' for all modes? Then we could rely on the base
+        # class implementation which returns nil by default.
+        [:installation, :live_installation, :update, :autoinst]
+      end
+
+      # Perform the final actions in the target system
+      def write
+        log.info("Starting umount_finish.rb")
+
+        remove_target_etc_mtab
+        set_btrfs_defaults_as_ro # No write access to the target after this!
+        close_scr_on_target
+        umount_target_mounts
+
+        log.info("umount_finish.rb done")
+        true
+      end
+
+      # Unmount all mounts to the target (typically using the /mnt prefix).
+      #
+      # This uses an Installation::Unmounter object which reads /proc/mounts.
+      # Relying on y2storage would be risky here since other processes like
+      # snapper or libzypp may have mounted filesystems without y2storage
+      # knowing about it.
+      #
+      def umount_target_mounts
+        dump_file("/proc/partitions")
+        dump_file("/proc/mounts")
+        unmounter = ::Installation::Unmounter.new(Installation.destdir)
+        log.info("Paths to unmount: #{unmounter.unmount_paths}")
+        return if unmounter.mounts.empty?
+
+        begin
+          unmounter.execute
+        rescue Cheetah::ExecutionFailed => e # Typically permissions problem
+          log.error(e.message)
+        end
+        unmounter.clear
+        unmounter.read_mounts_file("/proc/mounts")
+        unmount_summary(unmounter.unmount_paths)
+      end
+
+      # Write a summary of the unmount operations to the log.
+      def unmount_summary(leftover_paths)
+        if leftover_paths.empty?
+          log.info("All unmounts successful.")
+        else
+          log.warn("Leftover paths that could not be unmounted: 
#{leftover_paths}")
+          log_running_processes(leftover_paths)
+          dump_file("/proc/mounts")
         end
-        umount_list.sort!
-        log.info("umount_list:#{umount_list}")
+      end
+
+      # Dump a file in human-readable form to the log.
+      # Do not add the y2log header to each line so it can be easily used.
+      def dump_file(filename)
+        content = File.read(filename)
+        log.info("\n\n#{filename}:\n\n#{content}\n")
+      end
 
+      def remove_target_etc_mtab
         # symlink points to /proc, keep it (bnc#665437)
         if !FileUtils.IsLink("/etc/mtab")
           # remove [Installation::destdir]/etc/mtab which was faked for %post
@@ -113,256 +117,115 @@
           # hotfix: recreating /etc/mtab as symlink (bnc#725166)
           SCR.Execute(path(".target.bash"), "ln -s /proc/self/mounts 
/etc/mtab")
         end
+      end
 
-        # This must be done as long as the target root is still mounted
-        # (because the btrfs command requires that), but after the last write
-        # access to it (because it will be read only afterwards).
-        set_btrfs_defaults_as_ro
-
-        # Stop SCR on target
+      def close_scr_on_target
         WFM.SCRClose(Installation.scr_handle)
+      end
 
-        # first, umount everthing mounted *in* the target.
-        # /proc/bus/usb
-        # /proc
-
-        @umount_these = ["/proc", "/sys", "/dev", "/run"]
-
-        @umount_these.unshift(USB_PATH) if Hotplug.haveUSB
-
-        # exists in both inst-sys and target or in neither
-        @umount_these.unshift(EFIVARS_PATH) if File.exist?(EFIVARS_PATH)
-
-        Builtins.foreach(@umount_these) do |umount_dir|
-          umount_this = Builtins.sformat(
-            "%1%2",
-            Installation.destdir,
-            umount_dir
-          )
-          Builtins.y2milestone("Umounting: %1", umount_this)
-          umount_result = Convert.to_boolean(
-            WFM.Execute(path(".local.umount"), umount_this)
-          )
-          if umount_result != true
-            log_running_processes(umount_this)
-            # bnc #395034
-            # Don't remount them read-only!
-            if Builtins.contains(
-              ["/proc", "/sys", "/dev", USB_PATH, EFIVARS_PATH],
-              umount_dir
-            )
-              Builtins.y2warning("Umount failed, trying lazy umount...")
-              cmd = Builtins.sformat(
-                "sync; umount -l -f '%1';",
-                String.Quote(umount_this)
-              )
-            else
-              Builtins.y2warning(
-                "Umount failed, trying to remount read only..."
-              )
-              cmd = Builtins.sformat(
-                "sync; mount -o remount,noatime,ro '%1'; umount -l -f '%1';",
-                String.Quote(umount_this)
-              )
-            end
-            Builtins.y2milestone(
-              "Cmd: '%1' Ret: %2",
-              cmd,
-              WFM.Execute(path(".local.bash_output"), cmd)
-            )
-          end
-        end
-
-# storage-ng
-# rubocop:disable Style/BlockComments
-=begin
-
-        @targetMap = Storage.GetTargetMap
-
-        # first umount all file based crypto fs since they potentially
-        # could mess up umounting of normale filesystems if the crypt
-        # file is not on the root fs
-        Builtins.y2milestone("umount list %1", umount_list)
-        Builtins.foreach(
-          Ops.get_list(@targetMap, ["/dev/loop", "partitions"], [])
-        ) do |e|
-          if Ops.greater_than(Builtins.size(Ops.get_string(e, "mount", "")), 0)
-            Storage.Umount(Ops.get_string(e, "device", ""), true)
-            umount_list = Builtins.filter(umount_list) do |m|
-              m != Ops.get_string(e, "mount", "")
-            end
-            Builtins.y2milestone(
-              "loop umount %1 new list %2",
-              Ops.get_string(e, "mount", ""),
-              umount_list
-            )
-          end
-        end
-
-=end
+    public
 
-        # *** umount_list is lexically ordered !
-        # now umount in reverse order (guarantees "/" as last umount)
+      # For btrfs filesystems that should be read-only, set the root subvolume
+      # to read-only and change the /etc/fstab entry accordingly.
+      #
+      # Since we had to install RPMs to the target, we could not set it to
+      # read-only right away; but now we can, and we have to.
+      #
+      # This must be done as long as the target root is still mounted
+      # (because the btrfs command requires that), but after the last write
+      # access to it (because it will be read only afterwards).
+      def set_btrfs_defaults_as_ro
+        # This operation needs root privileges, but it's also generally not a
+        # good idea to do this even if you have the privileges: In that case,
+        # it would change your root subvolume to read-only which is not a good
+        # idea when just invoking this standalone for testing in a development
+        # environment.
+        return if @running_standalone
 
-        @umountLength = Builtins.size(umount_list)
-
-        while Ops.greater_than(@umountLength, 0)
-          @umountLength = Ops.subtract(@umountLength, 1)
-          @tmp = Ops.add(
-            Installation.destdir,
-            Ops.get(umount_list, @umountLength, "")
-          )
-
-          Builtins.y2milestone(
-            "umount target: %1, %2 more to go..",
-            @tmp,
-            @umountLength
-          )
-
-          @umount_status = Convert.to_boolean(
-            WFM.Execute(path(".local.umount"), @tmp)
-          )
-
-          # bnc #395034
-          # Don't remount them read-only!
-          next if @umount_status
-          log_running_processes(@tmp)
-
-          if Builtins.contains(
-            ["/proc", "/sys", "/dev", "/proc/bus/usb"],
-            @tmp
-          )
-            Builtins.y2warning("Umount failed, trying lazy umount...")
-            @cmd2 = Builtins.sformat(
-              "sync; umount -l -f '%1';",
-              String.Quote(@tmp)
-            )
-          else
-            Builtins.y2warning(
-              "Umount failed, trying to remount read only..."
-            )
-            @cmd2 = Builtins.sformat(
-              "mount -o remount,ro,noatime '%1'; umount -l -f '%1';",
-              String.Quote(@tmp)
-            )
-          end
-          Builtins.y2milestone(
-            "Cmd: '%1' Ret: %2",
-            @cmd2,
-            WFM.Execute(path(".local.bash_output"), @cmd2)
-          )
+        devicegraph = Y2Storage::StorageManager.instance.staging
 
+        ro_btrfs_filesystems = devicegraph.filesystems.select do |fs|
+          new_filesystem?(fs) && ro_btrfs_filesystem?(fs)
         end
 
-        # bugzilla #326478
-        Builtins.y2milestone(
-          "Currently mounted partitions: %1",
-          WFM.Execute(path(".local.bash_output"), "mount")
-        )
-
-        log_running_processes(Installation.destdir)
+        ro_btrfs_filesystems.each { |f| default_subvolume_as_ro(f) }
+      end
 
-# storage-ng
-=begin
+    protected
 
-        # must call .local.bash_output !
-        @max_loop_dev = Storage.NumLoopDevices
+      # [String] Name used by btrfs tools to name the filesystem tree.
+      BTRFS_FS_TREE = "(FS_TREE)".freeze
 
-        # disable loop device of crypto fs
-        @unload_crypto = false
-
-        while Ops.greater_than(@max_loop_dev, 0)
-          @unload_crypto = true
-          @exec_str = Builtins.sformat(
-            "/sbin/losetup -d /dev/loop%1",
-            Ops.subtract(@max_loop_dev, 1)
-          )
-          Builtins.y2milestone("loopdev: %1", @exec_str)
-          WFM.Execute(path(".local.bash"), @exec_str)
-          @max_loop_dev = Ops.subtract(@max_loop_dev, 1)
-        end
-
-        if @targetMap.any? { |_k, v| v["type"] == :CT_LVM }
-          Builtins.y2milestone("shutting down LVM")
-          WFM.Execute(path(".local.bash"), "/sbin/vgchange -a n")
-        end
+      # Set the "read-only" property for the root subvolume.
+      # This has to be done as long as the target root filesystem is still
+      # mounted.
+      #
+      # @param fs [Y2Storage::Filesystems::Btrfs] Btrfs filesystem to set 
read-only property on.
+      def default_subvolume_as_ro(fs)
+        output = Yast::Execute.on_target(
+          "btrfs", "subvolume", "get-default", fs.mount_point.path, stdout: 
:capture
+        )
+        default_subvolume = output.strip.split.last
+        # no btrfs_default_subvolume and no snapshots
+        default_subvolume = "" if default_subvolume == BTRFS_FS_TREE
 
-=end
+        subvolume_path = fs.btrfs_subvolume_mount_point(default_subvolume)
 
-      else
-        Builtins.y2error("unknown function: %1", @func)
-        @ret = nil
+        log.info("Setting root subvol read-only property on #{subvolume_path}")
+        Yast::Execute.on_target("btrfs", "property", "set", subvolume_path, 
"ro", "true")
       end
 
-      Builtins.y2debug("ret=%1", @ret)
-      Builtins.y2milestone("umount_finish finished")
-      deep_copy(@ret)
-    end
-
-    # Set the root subvolume to read-only and change the /etc/fstab entry
-    # accordingly
-    def set_btrfs_defaults_as_ro
-      devicegraph = Y2Storage::StorageManager.instance.staging
-
-      ro_btrfs_filesystems = devicegraph.filesystems.select do |fs|
-        new_filesystem?(fs) && ro_btrfs_filesystem?(fs)
+      # run "fuser" to get the details about open files
+      #
+      # @param mount_points <Array>[String]
+      def log_running_processes(mount_points)
+        paths = mount_points.join(" ")
+        fuser =
+          begin
+            # (the details are printed on STDERR, redirect it)
+            `LC_ALL=C fuser -v -m #{paths} 2>&1`
+          rescue => e
+            "fuser failed: #{e}"
+          end
+        log.warn("\n\nRunning processes using #{mount_points}:\n#{fuser}\n")
       end
 
-      ro_btrfs_filesystems.each { |f| default_subvolume_as_ro(f) }
-    end
-
-    # [String] Name used by btrfs tools to name the filesystem tree.
-    BTRFS_FS_TREE = "(FS_TREE)".freeze
-
-    # Set the "read-only" property for the root subvolume.
-    # This has to be done as long as the target root filesystem is still
-    # mounted.
-    #
-    # @param fs [Y2Storage::Filesystems::Btrfs] Btrfs filesystem to set 
read-only property on.
-    def default_subvolume_as_ro(fs)
-      output = Yast::Execute.on_target(
-        "btrfs", "subvolume", "get-default", fs.mount_point.path, stdout: 
:capture
-      )
-      default_subvolume = output.strip.split.last
-      # no btrfs_default_subvolume and no snapshots
-      default_subvolume = "" if default_subvolume == BTRFS_FS_TREE
-
-      subvolume_path = fs.btrfs_subvolume_mount_point(default_subvolume)
-
-      log.info("Setting root subvol read-only property on #{subvolume_path}")
-      Yast::Execute.on_target("btrfs", "property", "set", subvolume_path, 
"ro", "true")
-    end
-
-    # run "fuser" to get the details about open files
-    #
-    # @param mount_point [String]
-    def log_running_processes(mount_point)
-      fuser =
-        begin
-          # (the details are printed on STDERR, redirect it)
-          `LC_ALL=C fuser -v -m #{Shellwords.escape(mount_point)} 2>&1`
-        rescue => e
-          "fuser failed: #{e}"
-        end
-      log.warn("Running processes using #{mount_point}: #{fuser}")
-    end
-
-  private
-
-    # Check whether the given filesystem is going to be created
-    #
-    # @param filesystem [Y2Storage::Filesystems::Base]
-    # @return [Boolean]
-    def new_filesystem?(filesystem)
-      !filesystem.exists_in_probed?
-    end
+      # Check whether the given filesystem is going to be created
+      #
+      # @param filesystem [Y2Storage::Filesystems::Base]
+      # @return [Boolean]
+      def new_filesystem?(filesystem)
+        !filesystem.exists_in_probed?
+      end
 
-    # Check whether the given filesystem is read-only BTRFS
-    #
-    # @param filesystem [Y2Storage::Filesystems::Base]
-    # @return [Boolean]
-    def ro_btrfs_filesystem?(filesystem)
-      filesystem.is?(:btrfs) && filesystem.mount_point && 
filesystem.mount_options.include?("ro")
+      # Check whether the given filesystem is read-only BTRFS
+      #
+      # @param filesystem [Y2Storage::Filesystems::Base]
+      # @return [Boolean]
+      def ro_btrfs_filesystem?(filesystem)
+        filesystem.is?(:btrfs) && filesystem.mount_point && 
filesystem.mount_options.include?("ro")
+      end
     end
   end
 end
+
+#
+#------------------------------------------------------------------------------------
+#
+# This can be called standalone with
+#
+#   ruby /usr/share/YaST2/lib/installation/clients/umount_finish.rb
+#
+# or (even from the git checkout directory where this file is)
+#
+#   ruby ./umount_finish.rb
+#
+# with or without root permissions. Obviously, without root permissions, the
+# "umount" commands will fail. But you can observe in the user's ~/.y2log what
+# mounts would be unmounted. Make sure to mount something to /mnt to see 
anything.
+#
+if $PROGRAM_NAME == __FILE__ # Called direcly as standalone command? (not via 
rspec or require)
+  puts("Running UmountFinishClient standalone")
+  Installation::Clients::UmountFinishClient.new.run_standalone
+  puts("UmountFinishClient done")
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/src/lib/installation/unmounter.rb 
new/yast2-installation-4.4.17/src/lib/installation/unmounter.rb
--- old/yast2-installation-4.4.16/src/lib/installation/unmounter.rb     
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-4.4.17/src/lib/installation/unmounter.rb     
2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,197 @@
+# encoding: utf-8
+# 
------------------------------------------------------------------------------
+# Copyright (c) 2021 SUSE LLC
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of version 2 of the GNU General Public License as published by the
+# Free Software Foundation.
+#
+# 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.
+#
+# 
------------------------------------------------------------------------------
+
+require "yast"
+require "pathname"
+
+module Installation
+  # Class to handle unmounting all mounts from the given subtree on in the
+  # right order.
+  #
+  #   See also https://github.com/yast/yast-installation/pull/975
+  #
+  # This uses /proc/mounts by default to find active mounts, but for
+  # testability, it can also be fed from other files or line by line. It stores
+  # all necessary unmount actions so they can be executed all at once, and they
+  # can also be inspected without the execute step. This is intended for
+  # logging, debugging and testing.
+  #
+  # This relies on /proc/mounts already containing the entries in canonical
+  # order (which it always does), i.e. in the mount hierarchy from top to
+  # bottom. If you add entries manually, make sure to maintain that order.
+  #
+  # Sample usage:
+  #
+  #   unmounter = Installation::Unmounter.new("/mnt")
+  #   log.info("Paths to unmount: #{unmounter.unmount_paths}")
+  #   unmounter.execute
+  #
+  # Without specifying a file to read as the second parameter in the
+  # constructor, it will default to /proc/mounts which is the right thing for
+  # real life use.
+  #
+  class Unmounter
+    include Yast::Logger
+    # The mount prefix (typically "/mnt")
+    attr_reader :mnt_prefix
+    # @return [Array<Mount>] Relevant mounts to unmount
+    attr_reader :mounts
+    # @return [Array<Mount>] Ignored mounts (not starting with the mount 
prefix)
+    attr_reader :ignored_mounts
+
+    # Helper class to represent one mount, i.e. one entry in /proc/mounts.
+    class Mount
+      attr_reader :device, :mount_path, :fs_type, :mount_opt
+
+      def initialize(device, mount_path, fs_type = "", mount_opt = "")
+        @device = device
+        @mount_path = mount_path
+        @fs_type = fs_type
+        @mount_opt = mount_opt
+      end
+
+      # Format this mount as a string for logging.
+      #
+      def to_s
+        "<Mount #{@device} -> #{@mount_path} type #{@fs_type}>"
+      end
+    end
+
+    # Unmounter constructor.
+    #
+    # @param mnt_prefix [String] Prefix which paths should be unmounted.
+    #
+    # @param mounts_file_name [String] what to use instead of /proc/mounts.
+    #   Use 'nil' to not read any file at all; but in that case, later use
+    #   read_mounts_file() or add_mount().
+    #
+    def initialize(mnt_prefix = "/mnt", mounts_file_name = "/proc/mounts")
+      @mnt_prefix = Pathname.new(mnt_prefix).cleanpath.to_s
+      clear
+      read_mounts_file(mounts_file_name) unless mounts_file_name.nil?
+    end
+
+    # Clear all prevous content.
+    def clear
+      @mounts = []
+      @ignored_mounts = []
+    end
+
+    # Read a mounts file like /proc/mounts and add the relevant entries to the
+    # mounts stored in this class.
+    #
+    def read_mounts_file(file_name)
+      log.info("Reading file #{file_name}")
+      open(file_name).each { |line| add_mount(line) }
+    end
+
+    # Parse one entry of /proc/mounts and add it to @mounts
+    # if it meets the criteria (mount prefix)
+    #
+    # @param line [String] one line of /proc/mounts
+    # @return [Mount,nil] parsed mount if relevant
+    #
+    def add_mount(line)
+      mount = parse_mount(line)
+      return nil if mount.nil? # Empty or comment
+
+      if ignore?(mount)
+        @ignored_mounts << mount
+        return nil
+      end
+
+      log.info("Adding #{mount}")
+      @mounts << mount
+      mount
+    end
+
+    # Check if a mount should be ignored, i.e. if the path doesn't start with
+    # the mount prefix (usually "/mnt").
+    #
+    # @return [Boolean] ignore
+    #
+    def ignore?(mount)
+      return false if mount.mount_path == @mnt_prefix
+
+      !mount.mount_path.start_with?(@mnt_prefix + "/")
+    end
+
+    # Parse one entry of /proc/mounts.
+    #
+    # @param line [String] one line of /proc/mounts
+    # @return [Mount,nil] parsed mount
+    #
+    def parse_mount(line)
+      line.strip!
+      return nil if line.empty? || line.start_with?("#")
+
+      (device, mount_path, fs_type, mount_opt) = line.split
+      Mount.new(device, mount_path, fs_type, mount_opt)
+    end
+
+    # Return the paths to be unmounted in the correct unmount order.
+    #
+    # This makes use of the fact that /proc/mounts is already sorted in
+    # canonical order, i.e. from toplevel mount points to lower level ones.
+    #
+    # @return [Array<String>] paths
+    #
+    def unmount_paths
+      paths = @mounts.map(&:mount_path)
+      paths.reverse
+    end
+
+    # Return the paths that were ignored (in the order of /proc/mounts).
+    # This is mostly useful for debugging and testing.
+    #
+    # @return [Array<String>] paths
+    #
+    def ignored_paths
+      @ignored_mounts.map(&:mount_path)
+    end
+
+    # Actually execute all the pending unmount operations in the right
+    # sequence.
+    #
+    # This iterates over all relevant mounts and invokes the external "umount"
+    # command for each one separately. Notice that while "umount -R path" also
+    # exists, it will stop executing when it first encounters any mount that
+    # cannot be unmounted ("filesystem busy"), even if mounts that come after
+    # that could safely be unmounted.
+    #
+    # If unmounting a mount fails, this does not attempt to remount read-only
+    # ("umount -r"), by force ("umount -f") or lazily ("umount -l"):
+    #
+    # - Remounting read-only ("umount -r" or "mount -o remount,ro") typically
+    #   also fails if unmounting fails. It would have to be a rare coincidence
+    #   that a filesystem has only open files in read-only mode already; only
+    #   then it would have a chance to succeed.
+    #
+    # - Force-unmounting ("umount -f") realistically only works for NFS mounts.
+    #   It is intended for cases when the NFS server has become unreachable.
+    #
+    # - Lazy unmounting ("umount -l") mostly removes the entry for this
+    #   filesytem from /proc/mounts; it actually only unmounts when the pending
+    #   operations that prevent a simple unmount are finished which may take a
+    #   long time; or forever. And there is no way to find out if or when this
+    #   has ever happened, so the next mount for this filesystem may fail.
+    #
+    def execute
+      unmount_paths.each do |path|
+        log.info("Unmounting #{path}")
+        Yast::Execute.locally!("umount", path)
+      end
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/README.md 
new/yast2-installation-4.4.17/test/data/proc-mounts/README.md
--- old/yast2-installation-4.4.16/test/data/proc-mounts/README.md       
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-installation-4.4.17/test/data/proc-mounts/README.md       
2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,17 @@
+# test/data/proc-mounts
+
+This directory contains /proc/mounts files that were collected during an
+installation. The intended purpose is for testing the Unmounter class, but they
+may also be useful for other tests.
+
+All files named proc-mounts*-raw.txt are as taken directly from the system, the
+-pretty.txt variant is just formatted prettier (using `column -t`), and it
+contains some comment lines at the start to describe the scenario.
+
+`/proc/mounts` doesn't normally include comments, but the Unmounter class can
+handle comments and empty lines.
+
+Most scenarios include the partitions of the target system mounted below
+`/mnt`.
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-pretty.txt
 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-pretty.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-pretty.txt
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-pretty.txt
    2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,31 @@
+# /proc/mounts during installation with root on btrfs, no separate /home
+# formatted with  column -t
+#
+tmpfs       /                           tmpfs       
rw,relatime,size=2030608k,nr_inodes=0                                 0  0
+tmpfs       /                           tmpfs       
rw,relatime,size=2030608k,nr_inodes=0                                 0  0
+proc        /proc                       proc        rw,relatime                
                                           0  0
+sysfs       /sys                        sysfs       rw,relatime                
                                           0  0
+/dev/loop0  /parts/mp_0000              squashfs    ro,relatime                
                                           0  0
+/dev/loop1  /parts/mp_0001              squashfs    ro,relatime                
                                           0  0
+devtmpfs    /dev                        devtmpfs    
rw,relatime,size=959008k,nr_inodes=239752,mode=755                    0  0
+devpts      /dev/pts                    devpts      
rw,relatime,mode=600,ptmxmode=000                                     0  0
+rpc_pipefs  /var/lib/nfs/rpc_pipefs     rpc_pipefs  rw,relatime                
                                           0  0
+/dev/loop2  /mounts/mp_0000             squashfs    ro,relatime                
                                           0  0
+/dev/loop3  /mounts/mp_0001             squashfs    ro,relatime                
                                           0  0
+/dev/loop5  /mounts/mp_0003             squashfs    ro,relatime                
                                           0  0
+/dev/loop6  /mounts/mp_0004             squashfs    ro,relatime                
                                           0  0
+/dev/sda2   /mnt                        btrfs       
rw,relatime,space_cache,subvolid=268,subvol=/@/.snapshots/1/snapshot  0  0
+/dev/sda2   /mnt/.snapshots             btrfs       
rw,relatime,space_cache,subvolid=267,subvol=/@/.snapshots             0  0
+/dev/sda2   /mnt/boot/grub2/i386-pc     btrfs       
rw,relatime,space_cache,subvolid=266,subvol=/@/boot/grub2/i386-pc     0  0
+/dev/sda2   /mnt/boot/grub2/x86_64-efi  btrfs       
rw,relatime,space_cache,subvolid=265,subvol=/@/boot/grub2/x86_64-efi  0  0
+/dev/sda2   /mnt/home                   btrfs       
rw,relatime,space_cache,subvolid=264,subvol=/@/home                   0  0
+/dev/sda2   /mnt/opt                    btrfs       
rw,relatime,space_cache,subvolid=263,subvol=/@/opt                    0  0
+/dev/sda2   /mnt/root                   btrfs       
rw,relatime,space_cache,subvolid=262,subvol=/@/root                   0  0
+/dev/sda2   /mnt/srv                    btrfs       
rw,relatime,space_cache,subvolid=261,subvol=/@/srv                    0  0
+/dev/sda2   /mnt/tmp                    btrfs       
rw,relatime,space_cache,subvolid=260,subvol=/@/tmp                    0  0
+/dev/sda2   /mnt/usr/local              btrfs       
rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local              0  0
+/dev/sda2   /mnt/var                    btrfs       
rw,relatime,space_cache,subvolid=258,subvol=/@/var                    0  0
+devtmpfs    /mnt/dev                    devtmpfs    
rw,relatime,size=959008k,nr_inodes=239752,mode=755                    0  0
+proc        /mnt/proc                   proc        rw,relatime                
                                           0  0
+sysfs       /mnt/sys                    sysfs       rw,relatime                
                                           0  0
+tmpfs       /mnt/run                    tmpfs       
rw,relatime,size=2030608k,nr_inodes=0                                 0  0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-raw.txt 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-raw.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-raw.txt   
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-raw.txt   
    2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,28 @@
+tmpfs / tmpfs rw,relatime,size=2030608k,nr_inodes=0 0 0
+tmpfs / tmpfs rw,relatime,size=2030608k,nr_inodes=0 0 0
+proc /proc proc rw,relatime 0 0
+sysfs /sys sysfs rw,relatime 0 0
+/dev/loop0 /parts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop1 /parts/mp_0001 squashfs ro,relatime 0 0
+devtmpfs /dev devtmpfs rw,relatime,size=959008k,nr_inodes=239752,mode=755 0 0
+devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
+rpc_pipefs /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
+/dev/loop2 /mounts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop3 /mounts/mp_0001 squashfs ro,relatime 0 0
+/dev/loop5 /mounts/mp_0003 squashfs ro,relatime 0 0
+/dev/loop6 /mounts/mp_0004 squashfs ro,relatime 0 0
+/dev/sda2 /mnt btrfs 
rw,relatime,space_cache,subvolid=268,subvol=/@/.snapshots/1/snapshot 0 0
+/dev/sda2 /mnt/.snapshots btrfs 
rw,relatime,space_cache,subvolid=267,subvol=/@/.snapshots 0 0
+/dev/sda2 /mnt/boot/grub2/i386-pc btrfs 
rw,relatime,space_cache,subvolid=266,subvol=/@/boot/grub2/i386-pc 0 0
+/dev/sda2 /mnt/boot/grub2/x86_64-efi btrfs 
rw,relatime,space_cache,subvolid=265,subvol=/@/boot/grub2/x86_64-efi 0 0
+/dev/sda2 /mnt/home btrfs rw,relatime,space_cache,subvolid=264,subvol=/@/home 
0 0
+/dev/sda2 /mnt/opt btrfs rw,relatime,space_cache,subvolid=263,subvol=/@/opt 0 0
+/dev/sda2 /mnt/root btrfs rw,relatime,space_cache,subvolid=262,subvol=/@/root 
0 0
+/dev/sda2 /mnt/srv btrfs rw,relatime,space_cache,subvolid=261,subvol=/@/srv 0 0
+/dev/sda2 /mnt/tmp btrfs rw,relatime,space_cache,subvolid=260,subvol=/@/tmp 0 0
+/dev/sda2 /mnt/usr/local btrfs 
rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local 0 0
+/dev/sda2 /mnt/var btrfs rw,relatime,space_cache,subvolid=258,subvol=/@/var 0 0
+devtmpfs /mnt/dev devtmpfs rw,relatime,size=959008k,nr_inodes=239752,mode=755 
0 0
+proc /mnt/proc proc rw,relatime 0 0
+sysfs /mnt/sys sysfs rw,relatime 0 0
+tmpfs /mnt/run tmpfs rw,relatime,size=2030608k,nr_inodes=0 0 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-pretty.txt
 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-pretty.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-pretty.txt
   1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-pretty.txt
   2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,30 @@
+# /proc/mounts with partition-based btrfs and a separate /home with XFS
+# formatted with  column -t
+#
+tmpfs       /                           tmpfs       
rw,relatime,size=4020456k,nr_inodes=0                                 0  0
+proc        /proc                       proc        rw,relatime                
                                           0  0
+sysfs       /sys                        sysfs       rw,relatime                
                                           0  0
+/dev/loop0  /parts/mp_0000              squashfs    ro,relatime                
                                           0  0
+/dev/loop1  /parts/mp_0001              squashfs    ro,relatime                
                                           0  0
+devtmpfs    /dev                        devtmpfs    
rw,relatime,size=1945116k,nr_inodes=486279,mode=755                   0  0
+devpts      /dev/pts                    devpts      
rw,relatime,mode=600,ptmxmode=000                                     0  0
+rpc_pipefs  /var/lib/nfs/rpc_pipefs     rpc_pipefs  rw,relatime                
                                           0  0
+/dev/loop2  /mounts/mp_0000             squashfs    ro,relatime                
                                           0  0
+/dev/loop3  /mounts/mp_0001             squashfs    ro,relatime                
                                           0  0
+/dev/loop5  /mounts/mp_0003             squashfs    ro,relatime                
                                           0  0
+/dev/sr0    /var/adm/mount/AP_0xpRErh6  iso9660     
ro,relatime,nojoliet,check=s,map=n,blocksize=2048                     0  0
+/dev/sda2   /mnt                        btrfs       
rw,relatime,space_cache,subvolid=256,subvol=/@                        0  0
+/dev/sda2   /mnt/boot/grub2/i386-pc     btrfs       
rw,relatime,space_cache,subvolid=265,subvol=/@/boot/grub2/i386-pc     0  0
+/dev/sda2   /mnt/boot/grub2/x86_64-efi  btrfs       
rw,relatime,space_cache,subvolid=264,subvol=/@/boot/grub2/x86_64-efi  0  0
+/dev/sda3   /mnt/home                   xfs         
rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota              0  0
+/dev/sda2   /mnt/opt                    btrfs       
rw,relatime,space_cache,subvolid=263,subvol=/@/opt                    0  0
+/dev/sda2   /mnt/root                   btrfs       
rw,relatime,space_cache,subvolid=262,subvol=/@/root                   0  0
+/dev/sda2   /mnt/srv                    btrfs       
rw,relatime,space_cache,subvolid=261,subvol=/@/srv                    0  0
+/dev/sda2   /mnt/tmp                    btrfs       
rw,relatime,space_cache,subvolid=260,subvol=/@/tmp                    0  0
+/dev/sda2   /mnt/usr/local              btrfs       
rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local              0  0
+/dev/sda2   /mnt/var                    btrfs       
rw,relatime,space_cache,subvolid=258,subvol=/@/var                    0  0
+devtmpfs    /mnt/dev                    devtmpfs    
rw,relatime,size=1945116k,nr_inodes=486279,mode=755                   0  0
+proc        /mnt/proc                   proc        rw,relatime                
                                           0  0
+sysfs       /mnt/sys                    sysfs       rw,relatime                
                                           0  0
+tmpfs       /mnt/run                    tmpfs       
rw,relatime,size=4020456k,nr_inodes=0                                 0  0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-raw.txt
 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-raw.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-raw.txt
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-btrfs-xfs-home-raw.txt
      2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,27 @@
+tmpfs / tmpfs rw,relatime,size=4020456k,nr_inodes=0 0 0
+proc /proc proc rw,relatime 0 0
+sysfs /sys sysfs rw,relatime 0 0
+/dev/loop0 /parts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop1 /parts/mp_0001 squashfs ro,relatime 0 0
+devtmpfs /dev devtmpfs rw,relatime,size=1945116k,nr_inodes=486279,mode=755 0 0
+devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
+rpc_pipefs /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
+/dev/loop2 /mounts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop3 /mounts/mp_0001 squashfs ro,relatime 0 0
+/dev/loop5 /mounts/mp_0003 squashfs ro,relatime 0 0
+/dev/sr0 /var/adm/mount/AP_0xpRErh6 iso9660 
ro,relatime,nojoliet,check=s,map=n,blocksize=2048 0 0
+/dev/sda2 /mnt btrfs rw,relatime,space_cache,subvolid=256,subvol=/@ 0 0
+/dev/sda2 /mnt/boot/grub2/i386-pc btrfs 
rw,relatime,space_cache,subvolid=265,subvol=/@/boot/grub2/i386-pc 0 0
+/dev/sda2 /mnt/boot/grub2/x86_64-efi btrfs 
rw,relatime,space_cache,subvolid=264,subvol=/@/boot/grub2/x86_64-efi 0 0
+/dev/sda3 /mnt/home xfs 
rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota 0 0
+/dev/sda2 /mnt/opt btrfs rw,relatime,space_cache,subvolid=263,subvol=/@/opt 0 0
+/dev/sda2 /mnt/root btrfs rw,relatime,space_cache,subvolid=262,subvol=/@/root 
0 0
+/dev/sda2 /mnt/srv btrfs rw,relatime,space_cache,subvolid=261,subvol=/@/srv 0 0
+/dev/sda2 /mnt/tmp btrfs rw,relatime,space_cache,subvolid=260,subvol=/@/tmp 0 0
+/dev/sda2 /mnt/usr/local btrfs 
rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local 0 0
+/dev/sda2 /mnt/var btrfs rw,relatime,space_cache,subvolid=258,subvol=/@/var 0 0
+devtmpfs /mnt/dev devtmpfs rw,relatime,size=1945116k,nr_inodes=486279,mode=755 
0 0
+proc /mnt/proc proc rw,relatime 0 0
+sysfs /mnt/sys sysfs rw,relatime 0 0
+tmpfs /mnt/run tmpfs rw,relatime,size=4020456k,nr_inodes=0 0 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-pretty.txt
 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-pretty.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-pretty.txt
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-pretty.txt
    2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,32 @@
+# /proc/mounts during RPM installation on a btrfs on an encrypted LVM;
+# no snapshots (not enough disk space), separate /home (also btrfs)
+#
+# formatted with  column -t
+#
+tmpfs                    /                           tmpfs       
rw,relatime,size=4020488k,nr_inodes=0                                 0  0
+proc                     /proc                       proc        rw,relatime   
                                                        0  0
+sysfs                    /sys                        sysfs       rw,relatime   
                                                        0  0
+/dev/loop0               /parts/mp_0000              squashfs    ro,relatime   
                                                        0  0
+/dev/loop1               /parts/mp_0001              squashfs    ro,relatime   
                                                        0  0
+devtmpfs                 /dev                        devtmpfs    
rw,relatime,size=1945132k,nr_inodes=486283,mode=755                   0  0
+devpts                   /dev/pts                    devpts      
rw,relatime,mode=600,ptmxmode=000                                     0  0
+rpc_pipefs               /var/lib/nfs/rpc_pipefs     rpc_pipefs  rw,relatime   
                                                        0  0
+/dev/loop2               /mounts/mp_0000             squashfs    ro,relatime   
                                                        0  0
+/dev/loop3               /mounts/mp_0001             squashfs    ro,relatime   
                                                        0  0
+/dev/loop5               /mounts/mp_0003             squashfs    ro,relatime   
                                                        0  0
+/dev/sr0                 /var/adm/mount/AP_0xftqtdx  iso9660     
ro,relatime,nojoliet,check=s,map=n,blocksize=2048                     0  0
+/dev/mapper/system-root  /mnt                        btrfs       
rw,relatime,space_cache,subvolid=256,subvol=/@                        0  0
+/dev/mapper/system-root  /mnt/boot/grub2/i386-pc     btrfs       
rw,relatime,space_cache,subvolid=265,subvol=/@/boot/grub2/i386-pc     0  0
+/dev/mapper/system-root  /mnt/boot/grub2/x86_64-efi  btrfs       
rw,relatime,space_cache,subvolid=264,subvol=/@/boot/grub2/x86_64-efi  0  0
+/dev/mapper/system-home  /mnt/home                   btrfs       
rw,relatime,space_cache,subvolid=5,subvol=/                           0  0
+/dev/mapper/system-root  /mnt/opt                    btrfs       
rw,relatime,space_cache,subvolid=263,subvol=/@/opt                    0  0
+/dev/mapper/system-root  /mnt/root                   btrfs       
rw,relatime,space_cache,subvolid=262,subvol=/@/root                   0  0
+/dev/mapper/system-root  /mnt/srv                    btrfs       
rw,relatime,space_cache,subvolid=261,subvol=/@/srv                    0  0
+/dev/mapper/system-root  /mnt/tmp                    btrfs       
rw,relatime,space_cache,subvolid=260,subvol=/@/tmp                    0  0
+/dev/mapper/system-root  /mnt/usr/local              btrfs       
rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local              0  0
+/dev/mapper/system-root  /mnt/var                    btrfs       
rw,relatime,space_cache,subvolid=258,subvol=/@/var                    0  0
+devtmpfs                 /mnt/dev                    devtmpfs    
rw,relatime,size=1945132k,nr_inodes=486283,mode=755                   0  0
+proc                     /mnt/proc                   proc        rw,relatime   
                                                        0  0
+sysfs                    /mnt/sys                    sysfs       rw,relatime   
                                                        0  0
+tmpfs                    /mnt/run                    tmpfs       
rw,relatime,size=4020488k,nr_inodes=0                                 0  0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-raw.txt
 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-raw.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-raw.txt
       1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-enc-lvm-btrfs-raw.txt
       2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,27 @@
+tmpfs / tmpfs rw,relatime,size=4020488k,nr_inodes=0 0 0
+proc /proc proc rw,relatime 0 0
+sysfs /sys sysfs rw,relatime 0 0
+/dev/loop0 /parts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop1 /parts/mp_0001 squashfs ro,relatime 0 0
+devtmpfs /dev devtmpfs rw,relatime,size=1945132k,nr_inodes=486283,mode=755 0 0
+devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
+rpc_pipefs /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
+/dev/loop2 /mounts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop3 /mounts/mp_0001 squashfs ro,relatime 0 0
+/dev/loop5 /mounts/mp_0003 squashfs ro,relatime 0 0
+/dev/sr0 /var/adm/mount/AP_0xftqtdx iso9660 
ro,relatime,nojoliet,check=s,map=n,blocksize=2048 0 0
+/dev/mapper/system-root /mnt btrfs 
rw,relatime,space_cache,subvolid=256,subvol=/@ 0 0
+/dev/mapper/system-root /mnt/boot/grub2/i386-pc btrfs 
rw,relatime,space_cache,subvolid=265,subvol=/@/boot/grub2/i386-pc 0 0
+/dev/mapper/system-root /mnt/boot/grub2/x86_64-efi btrfs 
rw,relatime,space_cache,subvolid=264,subvol=/@/boot/grub2/x86_64-efi 0 0
+/dev/mapper/system-home /mnt/home btrfs 
rw,relatime,space_cache,subvolid=5,subvol=/ 0 0
+/dev/mapper/system-root /mnt/opt btrfs 
rw,relatime,space_cache,subvolid=263,subvol=/@/opt 0 0
+/dev/mapper/system-root /mnt/root btrfs 
rw,relatime,space_cache,subvolid=262,subvol=/@/root 0 0
+/dev/mapper/system-root /mnt/srv btrfs 
rw,relatime,space_cache,subvolid=261,subvol=/@/srv 0 0
+/dev/mapper/system-root /mnt/tmp btrfs 
rw,relatime,space_cache,subvolid=260,subvol=/@/tmp 0 0
+/dev/mapper/system-root /mnt/usr/local btrfs 
rw,relatime,space_cache,subvolid=259,subvol=/@/usr/local 0 0
+/dev/mapper/system-root /mnt/var btrfs 
rw,relatime,space_cache,subvolid=258,subvol=/@/var 0 0
+devtmpfs /mnt/dev devtmpfs rw,relatime,size=1945132k,nr_inodes=486283,mode=755 
0 0
+proc /mnt/proc proc rw,relatime 0 0
+sysfs /mnt/sys sysfs rw,relatime 0 0
+tmpfs /mnt/run tmpfs rw,relatime,size=4020488k,nr_inodes=0 0 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-inst-pretty.txt 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-inst-pretty.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-inst-pretty.txt 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-inst-pretty.txt 
    2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,14 @@
+# /proc/mounts during the installlation worflow before any target partitions 
are created or mounted;
+# formatted with  column -t
+#
+tmpfs       /                        tmpfs       
rw,relatime,size=4020488k,nr_inodes=0                0  0
+proc        /proc                    proc        rw,relatime                   
                       0  0
+sysfs       /sys                     sysfs       rw,relatime                   
                       0  0
+/dev/loop0  /parts/mp_0000           squashfs    ro,relatime                   
                       0  0
+/dev/loop1  /parts/mp_0001           squashfs    ro,relatime                   
                       0  0
+devtmpfs    /dev                     devtmpfs    
rw,relatime,size=1945132k,nr_inodes=486283,mode=755  0  0
+devpts      /dev/pts                 devpts      
rw,relatime,mode=600,ptmxmode=000                    0  0
+rpc_pipefs  /var/lib/nfs/rpc_pipefs  rpc_pipefs  rw,relatime                   
                       0  0
+/dev/loop2  /mounts/mp_0000          squashfs    ro,relatime                   
                       0  0
+/dev/loop3  /mounts/mp_0001          squashfs    ro,relatime                   
                       0  0
+/dev/loop5  /mounts/mp_0003          squashfs    ro,relatime                   
                       0  0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-inst-raw.txt 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-inst-raw.txt
--- 
old/yast2-installation-4.4.16/test/data/proc-mounts/proc-mounts-inst-raw.txt    
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/yast2-installation-4.4.17/test/data/proc-mounts/proc-mounts-inst-raw.txt    
    2021-08-26 15:46:35.000000000 +0200
@@ -0,0 +1,12 @@
+tmpfs / tmpfs rw,relatime,size=4020488k,nr_inodes=0 0 0
+proc /proc proc rw,relatime 0 0
+sysfs /sys sysfs rw,relatime 0 0
+/dev/loop0 /parts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop1 /parts/mp_0001 squashfs ro,relatime 0 0
+devtmpfs /dev devtmpfs rw,relatime,size=1945132k,nr_inodes=486283,mode=755 0 0
+devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0
+rpc_pipefs /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
+/dev/loop2 /mounts/mp_0000 squashfs ro,relatime 0 0
+/dev/loop3 /mounts/mp_0001 squashfs ro,relatime 0 0
+/dev/loop5 /mounts/mp_0003 squashfs ro,relatime 0 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-installation-4.4.16/test/lib/clients/umount_finish_test.rb 
new/yast2-installation-4.4.17/test/lib/clients/umount_finish_test.rb
--- old/yast2-installation-4.4.16/test/lib/clients/umount_finish_test.rb        
2021-08-17 20:51:42.000000000 +0200
+++ new/yast2-installation-4.4.17/test/lib/clients/umount_finish_test.rb        
2021-08-26 15:46:35.000000000 +0200
@@ -3,7 +3,7 @@
 require_relative "../../test_helper"
 require "installation/clients/umount_finish"
 
-describe Yast::UmountFinishClient do
+describe Installation::Clients::UmountFinishClient do
   before do
     Y2Storage::StorageManager.create_test_instance
   end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-installation-4.4.16/test/unmounter_test.rb 
new/yast2-installation-4.4.17/test/unmounter_test.rb
--- old/yast2-installation-4.4.16/test/unmounter_test.rb        1970-01-01 
01:00:00.000000000 +0100
+++ new/yast2-installation-4.4.17/test/unmounter_test.rb        2021-08-26 
15:46:35.000000000 +0200
@@ -0,0 +1,230 @@
+#! /usr/bin/env rspec
+
+require_relative "./test_helper"
+require "installation/unmounter"
+
+PROC_MOUNTS_PATH = File.join(File.expand_path(File.dirname(__FILE__)), 
"data/proc-mounts")
+PREFIX = "/mnt".freeze
+
+def stored_proc_mounts(scenario)
+  File.join(PROC_MOUNTS_PATH, "proc-mounts-#{scenario}-raw.txt")
+end
+
+def mount(mount_path)
+  Installation::Unmounter::Mount.new("/dev/something", mount_path, "FooFS")
+end
+
+describe Installation::Unmounter do
+  let(:proc_mounts) { nil }
+
+  describe "#new" do
+    let(:subject) { described_class.new(PREFIX, proc_mounts) }
+
+    context "empty" do
+      it "does not crash and burn" do
+        expect(subject.mounts).to eq []
+      end
+    end
+
+    context "before the installation is executed" do
+      let(:proc_mounts) { stored_proc_mounts("inst") }
+
+      it "does not find any relevant mounts" do
+        expect(subject.mounts).to eq []
+      end
+    end
+
+    context "when reading the actual /proc/mounts file" do
+      let(:proc_mounts) { "/proc/mounts" }
+
+      it "ignores /proc and /sys" do
+        expect(subject.ignored_paths).to include("/proc", "/sys")
+        # With checking for /, /proc, /sys, /dev this failed in Jenkins
+        # because that environment only seems to have /proc, /sys, /dev/pts, 
/dev/shm,
+        # so this test checks only for the least common denominator between
+        # a sane Linux system and that Jenkins environment.
+      end
+
+      # Don't check in this context that there is nothing to unmount:
+      # The machine that executes the test might actually have something 
mounted at /mnt.
+    end
+  end
+
+  describe "#mnt_prefix" do
+    it "leaves a normal mount prefix as it is" do
+      um = described_class.new("/foo", nil)
+      expect(um.mnt_prefix).to eq "/foo"
+    end
+
+    it "strips off one trailing slash" do
+      um = described_class.new("/foo/", nil)
+      expect(um.mnt_prefix).to eq "/foo"
+    end
+
+    it "even fixes up insanely broken prefixes" do
+      um = described_class.new("/foo///bar///", nil)
+      expect(um.mnt_prefix).to eq "/foo/bar"
+    end
+
+    it "leaves a root directory prefix as it is" do
+      um = described_class.new("/", nil)
+      expect(um.mnt_prefix).to eq "/"
+    end
+  end
+
+  describe "#ignore?" do
+    let(:subject) { described_class.new("/mnt", nil) }
+
+    it "does not ignore /mnt" do
+      expect(subject.ignore?(mount("/mnt"))).to eq false
+    end
+
+    it "does not ignore /mnt/foo" do
+      expect(subject.ignore?(mount("/mnt/foo"))).to eq false
+    end
+
+    it "ignores /mnt2" do
+      expect(subject.ignore?(mount("/mnt2"))).to eq true
+    end
+
+    it "ignores /mnt2/foo" do
+      expect(subject.ignore?(mount("/mnt2"))).to eq true
+    end
+
+    it "ignores an empty path" do
+      expect(subject.ignore?(mount(""))).to eq true
+    end
+  end
+
+  describe "#add_mount and #clear" do
+    before(:all) do
+      # Start with a completely empty unmounter
+      # and keep it alive between the tests of this group
+      @unmounter = described_class.new(PREFIX, nil)
+    end
+
+    it "starts completely empty" do
+      expect(@unmounter.mounts).to eq []
+      expect(@unmounter.ignored_mounts).to eq []
+      expect(@unmounter.unmount_paths).to eq []
+      expect(@unmounter.ignored_paths).to eq []
+    end
+
+    it "/ is ignored" do
+      @unmounter.add_mount("/dev/sdx1 / ext4 defaults 0 0")
+      expect(@unmounter.mounts).to eq []
+      expect(@unmounter.unmount_paths).to eq []
+      expect(@unmounter.ignored_paths).to eq ["/"]
+    end
+
+    it "/home is ignored" do
+      @unmounter.add_mount("/dev/sdx2 /home ext4 defaults 0 0")
+      expect(@unmounter.mounts).to eq []
+      expect(@unmounter.unmount_paths).to eq []
+      expect(@unmounter.ignored_paths).to eq ["/", "/home"]
+    end
+
+    it "/mnt will be unmounted" do
+      @unmounter.add_mount("/dev/sdy1 /mnt ext4 defaults 0 0")
+      expect(@unmounter.unmount_paths).to eq ["/mnt"]
+      expect(@unmounter.ignored_paths).to eq ["/", "/home"]
+    end
+
+    it "/mnt/boot will be unmounted" do
+      @unmounter.add_mount("/dev/sdy2 /mnt/boot ext4 defaults 0 0")
+      expect(@unmounter.unmount_paths).to eq ["/mnt/boot", "/mnt"]
+      expect(@unmounter.ignored_paths).to eq ["/", "/home"]
+    end
+
+    it "/mnt/boot/usb will be unmounted" do
+      @unmounter.add_mount("/dev/sdy2 /mnt/boot/usb ext4 defaults 0 0")
+      expect(@unmounter.unmount_paths).to eq ["/mnt/boot/usb", "/mnt/boot", 
"/mnt"]
+      expect(@unmounter.ignored_paths).to eq ["/", "/home"]
+    end
+
+    it "#clear clears everything" do
+      @unmounter.clear
+      expect(@unmounter.mounts).to eq []
+      expect(@unmounter.ignored_mounts).to eq []
+      expect(@unmounter.unmount_paths).to eq []
+      expect(@unmounter.ignored_paths).to eq []
+    end
+  end
+
+  describe "common scenarios" do
+    let(:subject) { described_class.new(PREFIX, proc_mounts) }
+
+    context "partition-based btrfs with subvolumes, no separate /home" do
+      let(:proc_mounts) { stored_proc_mounts("btrfs") } # see data/proc/mounts/
+      let(:expected_result) do
+        %w(/mnt/run
+           /mnt/sys
+           /mnt/proc
+           /mnt/dev
+           /mnt/var
+           /mnt/usr/local
+           /mnt/tmp
+           /mnt/srv
+           /mnt/root
+           /mnt/opt
+           /mnt/home
+           /mnt/boot/grub2/x86_64-efi
+           /mnt/boot/grub2/i386-pc
+           /mnt/.snapshots
+           /mnt)
+      end
+
+      it "will unmount /mnt/run, /mnt/sys, /mnt/proc, /mnt/dev, all 
subvolumes, /mnt" do
+        expect(subject.unmount_paths).to eq expected_result
+      end
+    end
+
+    context "partition-based btrfs with subvolumes and separate xfs /home" do
+      let(:proc_mounts) { stored_proc_mounts("btrfs-xfs-home") }
+      let(:expected_result) do
+        %w(/mnt/run
+           /mnt/sys
+           /mnt/proc
+           /mnt/dev
+           /mnt/var
+           /mnt/usr/local
+           /mnt/tmp
+           /mnt/srv
+           /mnt/root
+           /mnt/opt
+           /mnt/home
+           /mnt/boot/grub2/x86_64-efi
+           /mnt/boot/grub2/i386-pc
+           /mnt)
+      end
+
+      it "will unmount /mnt/run, /mnt/sys, /mnt/proc, /mnt/dev, all 
subvolumes, /mnt/home, /mnt" do
+        expect(subject.unmount_paths).to eq expected_result
+      end
+    end
+
+    context "encrypted LVM with btrfs with subvolumes and separate btrfs 
/home" do
+      let(:proc_mounts) { stored_proc_mounts("btrfs-xfs-home") }
+      let(:expected_result) do
+        %w(/mnt/run
+           /mnt/sys
+           /mnt/proc
+           /mnt/dev
+           /mnt/var
+           /mnt/usr/local
+           /mnt/tmp
+           /mnt/srv
+           /mnt/root
+           /mnt/opt
+           /mnt/home
+           /mnt/boot/grub2/x86_64-efi
+           /mnt/boot/grub2/i386-pc
+           /mnt)
+      end
+
+      it "will unmount /mnt/run, /mnt/sys, /mnt/proc, /mnt/dev, all 
subvolumes, /mnt/home, /mnt" do
+        expect(subject.unmount_paths).to eq expected_result
+      end
+    end
+  end
+end

Reply via email to