• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yast / yast-installation / 8705582428

16 Apr 2024 12:27PM UTC coverage: 40.913% (-0.2%) from 41.105%
8705582428

Pull #1114

github

shundhammer
Version bump and change log
Pull Request #1114: WIP: Handle autoinst AND autoupgrade (bsc#1222153)

0 of 1 new or added line in 1 file covered. (0.0%)

95 existing lines in 13 files now uncovered.

4489 of 10972 relevant lines covered (40.91%)

6.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.72
/src/lib/installation/clients/umount_finish.rb
1
# ------------------------------------------------------------------------------
2
# Copyright (c) 2006-2012 Novell, Inc. All Rights Reserved.
3
# Copyright (c) 2013-2021 SUSE LLC
4
#
5
# This program is free software; you can redistribute it and/or modify it under
6
# the terms of version 2 of the GNU General Public License as published by the
7
# Free Software Foundation.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
12
# ------------------------------------------------------------------------------
13

14
require "yast"
1✔
15
require "installation/finish_client"
1✔
16
require "installation/unmounter"
1✔
17
require "y2storage"
1✔
18

19
module Installation
1✔
20
  module Clients
1✔
21
    # Finish client to unmount all mounts to the target
22
    class UmountFinishClient < FinishClient
1✔
23
      include Yast::Logger
1✔
24

25
      # Constructor
26
      def initialize
1✔
27
        super
8✔
28
        textdomain "installation"
8✔
29
        Yast.import "FileUtils"
8✔
30
        @running_standalone = false
8✔
31
      end
32

33
      # This can be used when invoking this file directly with
34
      #   ruby ./umount_finish.rb
35
      #
36
      def run_standalone
1✔
37
        @running_standalone = true
×
38
        Installation.destdir = "/mnt" if Installation.destdir == "/"
×
39
        write
×
40
      end
41

42
    protected
1✔
43

44
      def title
1✔
45
        # progress step title
46
        _("Unmounting all mounted devices...")
×
47
      end
48

49
      def modes
1✔
50
        # FIXME: better use 'nil' for all modes? Then we could rely on the base
51
        # class implementation which returns nil by default.
52
        [:installation, :live_installation, :update, :autoinst]
×
53
      end
54

55
      # Perform the final actions in the target system
56
      def write
1✔
57
        log.info("Starting umount_finish.rb")
×
58

59
        remove_target_etc_mtab
×
60
        set_btrfs_defaults_as_ro # No write access to the target after this!
×
61
        close_scr_on_target
×
62
        umount_target_mounts
×
63

64
        log.info("umount_finish.rb done")
×
65
        true
×
66
      end
67

68
      # Unmount all mounts to the target (typically using the /mnt prefix).
69
      #
70
      # This uses an Installation::Unmounter object which reads /proc/mounts.
71
      # Relying on y2storage would be risky here since other processes like
72
      # snapper or libzypp may have mounted filesystems without y2storage
73
      # knowing about it.
74
      #
75
      def umount_target_mounts
1✔
76
        dump_file("/proc/partitions")
×
77
        dump_file("/proc/mounts")
×
78
        unmounter = ::Installation::Unmounter.new(Installation.destdir)
×
79
        log.info("Paths to unmount: #{unmounter.unmount_paths}")
×
80
        return if unmounter.mounts.empty?
×
81

UNCOV
82
        begin
×
83
          unmounter.execute
×
84
        rescue Cheetah::ExecutionFailed => e # Typically permissions problem
85
          log.error(e.message)
×
86
        end
87
        unmounter.clear
×
88
        unmounter.read_mounts_file("/proc/mounts")
×
89
        unmount_summary(unmounter.unmount_paths)
×
90
      end
91

92
      # Write a summary of the unmount operations to the log.
93
      def unmount_summary(leftover_paths)
1✔
94
        if leftover_paths.empty?
×
95
          log.info("All unmounts successful.")
×
96
        else
97
          log.warn("Leftover paths that could not be unmounted: #{leftover_paths}")
×
98
          log_running_processes(leftover_paths)
×
99
          dump_file("/proc/mounts")
×
100
        end
101
      end
102

103
      # Dump a file in human-readable form to the log.
104
      # Do not add the y2log header to each line so it can be easily used.
105
      def dump_file(filename)
1✔
106
        content = File.read(filename)
×
107
        log.info("\n\n#{filename}:\n\n#{content}\n")
×
108
      end
109

110
      def remove_target_etc_mtab
1✔
111
        # symlink points to /proc, keep it (bnc#665437)
112
        return if FileUtils.IsLink("/etc/mtab")
×
113

114
        # remove [Installation::destdir]/etc/mtab which was faked for %post
115
        # scripts in inst_rpmcopy
116
        SCR.Execute(path(".target.remove"), "/etc/mtab")
×
117

118
        # hotfix: recreating /etc/mtab as symlink (bnc#725166)
119
        SCR.Execute(path(".target.bash"), "ln -s /proc/self/mounts /etc/mtab")
×
120
      end
121

122
      def close_scr_on_target
1✔
123
        WFM.SCRClose(Installation.scr_handle)
×
124
      end
125

126
    public
1✔
127

128
      # For btrfs filesystems that should be read-only, set the root subvolume
129
      # to read-only and change the /etc/fstab entry accordingly.
130
      #
131
      # Since we had to install RPMs to the target, we could not set it to
132
      # read-only right away; but now we can, and we have to.
133
      #
134
      # This must be done as long as the target root is still mounted
135
      # (because the btrfs command requires that), but after the last write
136
      # access to it (because it will be read only afterwards).
137
      def set_btrfs_defaults_as_ro
1✔
138
        # This operation needs root privileges, but it's also generally not a
139
        # good idea to do this even if you have the privileges: In that case,
140
        # it would change your root subvolume to read-only which is not a good
141
        # idea when just invoking this standalone for testing in a development
142
        # environment.
143
        return if @running_standalone
8✔
144

145
        devicegraph = Y2Storage::StorageManager.instance.staging
8✔
146

147
        ro_btrfs_filesystems = devicegraph.filesystems.select do |fs|
8✔
148
          new_filesystem?(fs) && ro_btrfs_filesystem?(fs)
8✔
149
        end
150

151
        ro_btrfs_filesystems.each { |f| default_subvolume_as_ro(f) }
13✔
152
      end
153

154
    protected
1✔
155

156
      # [String] Name used by btrfs tools to name the filesystem tree.
157
      BTRFS_FS_TREE = "(FS_TREE)".freeze
1✔
158

159
      # Set the "read-only" property for the root subvolume.
160
      # This has to be done as long as the target root filesystem is still
161
      # mounted.
162
      #
163
      # @param filesystem [Y2Storage::Filesystems::Btrfs] Btrfs filesystem to set
164
      #                                                   read-only property on.
165
      def default_subvolume_as_ro(filesystem)
1✔
166
        output = Yast::Execute.on_target(
5✔
167
          "btrfs", "subvolume", "get-default", filesystem.mount_point.path, stdout: :capture
168
        )
169
        default_subvolume = output.strip.split.last
5✔
170
        # no btrfs_default_subvolume and no snapshots
171
        default_subvolume = "" if default_subvolume == BTRFS_FS_TREE
5✔
172

173
        subvolume_path = filesystem.btrfs_subvolume_mount_point(default_subvolume)
5✔
174

175
        log.info("Setting root subvol read-only property on #{subvolume_path}")
5✔
176
        Yast::Execute.on_target("btrfs", "property", "set", subvolume_path, "ro", "true")
5✔
177
      end
178

179
      # run "fuser" to get the details about open files
180
      #
181
      # @param mount_points <Array>[String]
182
      def log_running_processes(mount_points)
1✔
183
        paths = mount_points.join(" ")
×
UNCOV
184
        fuser =
×
185
          begin
186
            # (the details are printed on STDERR, redirect it)
187
            `LC_ALL=C fuser -v -m #{paths} 2>&1`
×
188
          rescue StandardError => e
189
            "fuser failed: #{e}"
×
190
          end
191
        log.warn("\n\nRunning processes using #{mount_points}:\n#{fuser}\n")
×
192
      end
193

194
      # Check whether the given filesystem is going to be created
195
      #
196
      # @param filesystem [Y2Storage::Filesystems::Base]
197
      # @return [Boolean]
198
      def new_filesystem?(filesystem)
1✔
199
        !filesystem.exists_in_probed?
8✔
200
      end
201

202
      # Check whether the given filesystem is read-only BTRFS
203
      #
204
      # @param filesystem [Y2Storage::Filesystems::Base]
205
      # @return [Boolean]
206
      def ro_btrfs_filesystem?(filesystem)
1✔
207
        filesystem.is?(:btrfs) && filesystem.mount_point && filesystem.mount_options.include?("ro")
7✔
208
      end
209
    end
210
  end
211
end
212

213
#
214
#------------------------------------------------------------------------------------
215
#
216
# This can be called standalone with
217
#
218
#   ruby /usr/share/YaST2/lib/installation/clients/umount_finish.rb
219
#
220
# or (even from the git checkout directory where this file is)
221
#
222
#   ruby ./umount_finish.rb
223
#
224
# with or without root permissions. Obviously, without root permissions, the
225
# "umount" commands will fail. But you can observe in the user's ~/.y2log what
226
# mounts would be unmounted. Make sure to mount something to /mnt to see anything.
227
#
228
if $PROGRAM_NAME == __FILE__ # Called direcly as standalone command? (not via rspec or require)
1✔
229
  puts("Running UmountFinishClient standalone")
×
230
  Installation::Clients::UmountFinishClient.new.run_standalone
×
231
  puts("UmountFinishClient done")
×
232
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc