-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathfstabs.rb
More file actions
515 lines (438 loc) · 18.6 KB
/
Copy pathfstabs.rb
File metadata and controls
515 lines (438 loc) · 18.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# Copyright (c) [2018-2019] SUSE LLC
#
# All Rights Reserved.
#
# 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.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
require "yast"
require "yast/i18n"
require "y2partitioner/device_graphs"
require "y2partitioner/filesystems"
require "y2partitioner/actions/controllers/base"
require "y2storage"
Yast.import "Arch"
module Y2Partitioner
module Actions
module Controllers
# This class stores information about the fstab files read from all
# the filesystem in the system. It also saves information about the
# selected fstab to be used to import mount points.
class Fstabs < Base
include Yast::I18n
# @return [Y2Storage::Fstab] fstab file selected to import mount points
attr_reader :selected_fstab
# @return [Boolean] whether the system volumes must be formatted
#
# @note A volume is considered as "system volume" when it is mounted in certain
# specific mount point like /, /usr, etc. See {#system_mount_points}.
attr_accessor :format_system_volumes
alias_method :format_system_volumes?, :format_system_volumes
# Constructor
def initialize
super()
textdomain "storage"
end
# All fstab files found in the system
#
# @return [Array<Y2Storage::Fstab>]
def fstabs
disk_analyzer.fstabs
end
# Sets the selected fstab
#
# Note that the system graph with crypttab names need to be reset after selecting a new fstab.
#
# @param [Y2Storage::Fstab] new_fstab
def selected_fstab=(new_fstab)
reset_system_graph_with_crypttab_names
@selected_fstab = new_fstab
end
# Selects the previous fstab
#
# The current selected fstab does not change if it already is the first one.
#
# @return [Y2Storage::Fstab]
def select_prev_fstab
current_index = fstabs.index(selected_fstab)
prev_index = [0, current_index - 1].max
self.selected_fstab = fstabs.at(prev_index)
end
# Selects the next fstab
#
# The current selected fstab does not change if it already is the last one.
#
# @return [Y2Storage::Fstab]
def select_next_fstab
current_index = fstabs.index(selected_fstab)
next_index = [fstabs.size - 1, current_index + 1].min
self.selected_fstab = fstabs.at(next_index)
end
# Checks whether the selected fstab is the first one
#
# @return [Boolean]
def selected_first_fstab?
selected_fstab == fstabs.first
end
# Checks whether the selected fstab is the last one
#
# @return [Boolean]
def selected_last_fstab?
selected_fstab == fstabs.last
end
# Errors in the selected fstab
#
# @see #not_importable_entries_error
#
# @return [Array<String>]
def selected_fstab_errors
[not_importable_entries_error].compact
end
# Imports mount points from the selected fstab
#
# Before importing, the current devicegraph is reset to the system one.
def import_mount_points
reset_current_graph
importable_entries.each { |e| import_mount_point(e) }
Y2Storage::Filesystems::Btrfs.refresh_subvolumes_shadowing(current_graph)
end
private
# System mount points are taken from old code, see
# https://github.com/yast/yast-storage/blob/master_old/src/modules/FileSystems.rb#L438
SYSTEM_MOUNT_POINTS = ["/", "/usr", "/var", "/opt", "/boot"].freeze
private_constant :SYSTEM_MOUNT_POINTS
# Error when some entries in the selected fstab cannot be imported
#
# An entry cannot be imported when the device is not found or it is used
# by other device (e.g., used by LVM or MD RAID).
#
# @return [String, nil] nil if all entries can be imported
def not_importable_entries_error
entries = not_importable_entries
return nil if entries.empty?
mount_points = entries.map(&:mount_point).join("\n")
# TRANSLATORS: %{mount_points} is replaced by a list of mount points, please
# do not modify it.
format(_("The following mount points cannot be imported:\n%{mount_points}"),
mount_points:)
end
# Entries in the current selected fstab that can be imported
#
# @see #can_be_imported?
#
# @return[Array<Y2Storage::SimpleEtcFstabEntry>]
def importable_entries
selected_fstab.filesystem_entries.select { |e| can_be_imported?(e) }
end
# Entries in the current selected fstab that cannot be imported
#
# @see #can_be_imported?
#
# @return[Array<Y2Storage::SimpleEtcFstabEntry>]
def not_importable_entries
selected_fstab.filesystem_entries - importable_entries
end
# Whether a fstab entry can be imported
#
# An entry can be imported when the device is known and it is not used
# by other device (e.g., used by LVM or MD RAID), or it is a known NFS.
# Moreover, in case the device must be formatted, the entry also must
# indicate a known filesystem type.
#
# @param entry [Y2Storage::SimpleEtcFstabEntry]
# @return [Boolean]
def can_be_imported?(entry)
device = entry.device(system_graph_with_crypttab_names)
return false unless device
return true unless must_be_formatted?(device, entry.mount_point)
usable_fs_type?(entry) && can_be_formatted?(device)
end
# Whether the device must be formatted in order to import the mount point
#
# A device must be formatted when it is not currently formatted or it is a device
# that should be mounted over a system mount point (see #{system_mount_points}).
# For this last case, the option to format system volumes should be selected
# (see {#format_system_volumes?}).
#
# @param device [Y2Storage::BlkDevice, Y2Storage::Filesystems::Base]
# @param mount_point [String] mount point of fstab entry
#
# @return [Boolean]
def must_be_formatted?(device, mount_point)
# In case the device is a filesystem (i.e., NFS), the device should not be formatted.
return false if device.is?(:filesystem)
!device.formatted? ||
(system_mount_point?(mount_point) && format_system_volumes?)
end
# Whether the mount point is included in the list of system mount points
#
# @param mount_point [String] mount point of fstab entry
# @return [Boolean]
def system_mount_point?(mount_point)
system_mount_points.include?(mount_point)
end
# Mount points considered as system mount points
#
# The list of system mount points are taken from old code, see
# https://github.com/yast/yast-storage/blob/master_old/src/modules/FileSystems.rb#L438
#
# @return [Array<String>]
def system_mount_points
return @system_mount_points if @system_mount_points
@system_mount_points = SYSTEM_MOUNT_POINTS.dup
@system_mount_points << "/boot/zipl" if Yast::Arch.s390
@system_mount_points
end
# Whether a fstab entry has an usable filesystem type
#
# A filesystem type is usable when it is known and supported.
#
# @param entry [Y2Storage::SimpleEtcFstabEntry]
# @return [Boolean]
def usable_fs_type?(entry)
known_fs_type?(entry) && supported_fs_type?(entry)
end
# Whether a fstab entry has a known filesystem type
#
# In case the fstab entry contains "auto" or "none" in the third
# field (fs_vfstype), the filesystem type cannot be determined.
#
# @param entry [Y2Storage::SimpleEtcFstabEntry]
# @return [Boolean]
def known_fs_type?(entry)
!entry.fs_type.is?(:auto) && !entry.fs_type.is?(:unknown)
end
# Whether a fstab entry has a filesystem type supported by the Partitioner
#
# Only some filesystem types are supported by Partitioner, see {Filesystems.all}.
#
# @param entry [Y2Storage::SimpleEtcFstabEntry]
# @return [Boolean]
def supported_fs_type?(entry)
Filesystems.supported?(entry.fs_type)
end
# Whether a device can be formatted
#
# A device can be formatted if it is already formatted or it is not used by
# another device (e.g., LVM or MD RAID). Moreover, in case of a encryption
# device, the device must be active.
#
# @param device [Y2Storage::BlkDevice]
# @return [Boolean]
def can_be_formatted?(device)
return false if device.is?(:encryption) && !device.active?
unused?(device) || device.formatted?
end
# Whether the device has not been used yet
#
# @param device [Y2Storage::BlkDevice]
# @return [Boolean]
def unused?(device)
device.descendants.empty?
end
# Initializes current devicegraph with system
def reset_current_graph
DeviceGraphs.instance.current = system_graph.dup
end
# Resets the system graph containing crypttab names
def reset_system_graph_with_crypttab_names
@system_graph_with_crypttab_names = nil
end
# Imports the mount point of a fstab entry
#
# When the device needs to be formatted (see {#must_be_formatted?}), the filesystem type
# indicated in the entry is used. In case the device is not a block device (e.g., NFS),
# the device is not formatted and only the mount point, mount by method and mount options
# are assigned.
#
# @param entry [Y2Storage::SimpleEtcFstabEntry]
def import_mount_point(entry)
device = entry.device(system_graph_with_crypttab_names)
return unless device
device = device_from_current_graph(device)
if must_be_formatted?(device, entry.mount_point)
filesystem = format_device(device, entry)
create_mount_point(filesystem, entry)
setup_blk_filesystem(filesystem)
else
filesystem = device.is?(:filesystem) ? device : device.filesystem
create_mount_point(filesystem, entry)
end
end
# Device version from the current devicegraph
#
# @param device [Y2Storage::Device]
# @return [Y2Storage::Device]
def device_from_current_graph(device)
if missing_swap_encryption?(device)
copy_swap_encryption(device)
else
current_graph.find_device(device.sid)
end
end
# Copies a plain encryption for swap into the current devicegraph
#
# @param device [Y2Storage::Encryption]
# @return [Y2Storage::Encryption]
def copy_swap_encryption(device)
blk_device = device_from_current_graph(device.blk_device)
blk_device.remove_descendants
device.copy_to(current_graph)
end
# Formats the device indicated in the fstab entry
#
# The filesystem label is preserved.
#
# @param device [Y2Storage::BlkDevice]
# @param entry [Y2Storage::SimpleEtcFstabEntry]
#
# @return [Y2Storage::Filesystems::Base]
def format_device(device, entry)
label = filesystem_label(device)
device.delete_filesystem
filesystem = device.create_filesystem(entry.fs_type)
filesystem.label = label if label
filesystem
end
# Label of the current filesystem (if any)
#
# @param device [Y2Storage::BlkDevice]
# @return [String, nil] nil if the device is not formatted
def filesystem_label(device)
return nil unless device.formatted?
device.filesystem.label
end
# Creates the #{Y2Storage::MountPoint} object based on the imported
# fstab entry.
#
# @param filesystem [Y2Storage::Filesystems::Base]
# @param entry [Y2Storage::SimpleEtcFstabEntry]
def create_mount_point(filesystem, entry)
filesystem.mount_path = entry.mount_point
filesystem.mount_point.mount_by = entry.mount_by if entry.mount_by
filesystem.mount_point.mount_options = entry.mount_options if entry.mount_options.any?
end
# Performs any additional final step needed for the new block filesystem (Btrfs stuff, so far)
#
# @param filesystem [Y2Storage::Filesystems::BlkFilesystem]
def setup_blk_filesystem(filesystem)
add_filesystem_devices(filesystem)
if filesystem.can_configure_snapper?
filesystem.configure_snapper = filesystem.default_configure_snapper?
end
filesystem.setup_default_btrfs_subvolumes if filesystem.supports_btrfs_subvolumes?
end
# Adds missing devices to the filesystem when the original filesystem was multidevice
#
# @param filesystem [Y2Storage::Filesystems::BlkFilesystem]
def add_filesystem_devices(filesystem)
original_filesystem = original_filesystem(filesystem)
return unless add_devices?(filesystem)
devices = original_filesystem.blk_devices.map { |d| current_graph.find_device(d.sid) }.compact
devices.each { |d| add_filesystem_device(filesystem, d) }
end
# Whether more devices should be added to a multi-device filesystem
#
# @param filesystem [Y2Storage::Filesystems::BlkFilesystem]
def add_devices?(filesystem)
original_filesystem = original_filesystem(filesystem)
return false unless original_filesystem&.multidevice?
# FIXME: check the fstab entry UUID instead
filesystem.type == original_filesystem&.type
end
# Adds a device to the filesystem
#
# @param filesystem [Y2Storage::Filesystems::BlkFilesystem]
# @param device [Y2Storage::BlkDevice]
def add_filesystem_device(filesystem, device)
return unless filesystem.respond_to?(:add_device)
return if filesystem.blk_devices.map(&:sid).include?(device.sid)
filesystem.add_device(device)
end
# Original version of the filesystem in the system graph
#
# @param filesystem [Y2Storage::Filesystems::BlkFilesystem]
# @return [Y2Storage::Filesystems::BlkFilesystem, nil] nil if the filesystem cannot be found in
# system graph.
def original_filesystem(filesystem)
original_device = system_device(filesystem.blk_devices.first)
return nil unless original_device&.formatted?
original_device.filesystem
end
# System graph with information about crypttab names indicated in the selected crypttab file
#
# When the device name in a fstab entry corresponds to a encryption device, the device could be
# not found by its fstab name. In general, encryptions might be probed with a different name, so
# before searching for the device, the encryption names from the crypttab file are saved into the
# proper encryption device. Also note that a random encryption layer can be created when a
# crypttab entry points to a swap device encrypted with random password. All these changes are
# made in a temporary devicegraph, so the original system graph is never altered.
#
# @see #add_crypttab_names_to
#
# @return [Y2Storage::Devicegraph]
def system_graph_with_crypttab_names
@system_graph_with_crypttab_names ||= add_crypttab_names_to(system_graph)
end
# Duplicates the given devicegraph and saves the encryption names from the crypttab file
#
# @see #crypttab
#
# @param devicegraph [Y2Storage::Devicegraph]
# @return [Y2Storage::Devicegraph]
def add_crypttab_names_to(devicegraph)
fixed_devicegraph = devicegraph.dup
return fixed_devicegraph unless crypttab
crypttab.save_encryption_names(fixed_devicegraph)
fixed_devicegraph
end
# Selects the crypttab contained in the same filesystem than the selected fstab
#
# @return [Y2Storage::Crypttab, nil] nil if the filesystem does not contain a crypttab file.
def crypttab
disk_analyzer.crypttabs.find { |c| c.filesystem == selected_fstab.filesystem }
end
# Whether the given device represents a not probed encryption generated by a swap encryption
# method
#
# Note that no headers are written into the device when using plain encryption (which is the
# underlying technology used for randomly encrypted swaps). For this reason, plain encryption
# devices are only probed for the root filesystem by parsing its crypttab file.
#
# A plain encryption device might be created when searching for a device from a fstab entry, see
# {Y2Storage::Crypttab.save_encryption_names}.
#
# @param device [Y2Storage::Device]
# @return [Boolean]
def missing_swap_encryption?(device)
missing_device?(device) && swap_encryption?(device)
end
# Whether the given device is missing in the probed devicegraph
#
# @param device [Y2Storage::Device]
# @return [Boolean]
def missing_device?(device)
!device.exists_in_devicegraph?(system_graph)
end
# Whether the given device is an encryption generated by a swap encryption method
#
# @param device [Y2Storage::Encryption]
# @return [Boolean]
def swap_encryption?(device)
device.is?(:encryption) && device.method.only_for_swap?
end
end
end
end
end