-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,92 +3,128 @@ | |
Yast.import "BootCommon" | ||
|
||
module Bootloader | ||
# Responsibility of class is to manage backups of MBR, respective PBR of disk, | ||
# Responsibility of class is to manage backup of MBR, respective PBR of disk, | ||
# respective partition. | ||
class BootRecordBackup | ||
class << self | ||
include Yast::Logger | ||
BASH_PATH = Yast::Path.new(".target.bash") | ||
BASH_OUTPUT_PATH = Yast::Path.new(".target.bash_output") | ||
MAIN_BACKUP_DIR = "/var/lib/YaST2/backup_boot_sectors/" | ||
KEPT_BACKUPS = 10 | ||
|
||
# Creates backup of MBR or PBR of given device. | ||
# Backup is stored in /var/lib/YaST2/backup_boot_sectors, in logs | ||
# directory and if it is MBR of primary disk, then also in /boot/backup_mbr | ||
def create_for(device) | ||
device_file = device.tr("/", "_") | ||
device_file_path = MAIN_BACKUP_DIR + device_file | ||
|
||
Yast::SCR.Execute(BASH_PATH, "mkdir -p #{MAIN_BACKUP_DIR}") | ||
|
||
# check if file exists | ||
if Yast::SCR.Read(Yast::Path.new(".target.size"), device_file_path) > 0 | ||
# move it so we do not overwrite it | ||
change_date = formated_file_ctime(device_file_path) | ||
Yast::SCR.Execute( | ||
BASH_PATH, | ||
Yast::Builtins.sformat("/bin/mv %1 %1-%2", device_file_path, change_date) | ||
) | ||
include Yast::Logger | ||
BASH_PATH = Yast::Path.new(".target.bash") | ||
BASH_OUTPUT_PATH = Yast::Path.new(".target.bash_output") | ||
TARGET_SIZE = Yast::Path.new(".target.size") | ||
MAIN_BACKUP_DIR = "/var/lib/YaST2/backup_boot_sectors/" | ||
KEPT_BACKUPS = 10 | ||
|
||
cleanup_backups(device_file) | ||
end | ||
attr_reader :device | ||
|
||
copy_br(device, device_file_path) | ||
# Create backup handling class for given device | ||
# @param device[String] expect kernel name of device like "/dev/sda" | ||
def initialize(device) | ||
@device = device | ||
end | ||
|
||
# save MBR to yast2 log directory | ||
logs_path = "/var/log/YaST2/" + device_file | ||
copy_br(device, logs_path) | ||
# Creates backup of MBR or PBR of given device. | ||
# Backup is stored in /var/lib/YaST2/backup_boot_sectors, in logs | ||
# directory and if it is MBR of primary disk, then also in /boot/backup_mbr | ||
def create | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
jreidinger
Author
Member
|
||
Yast::SCR.Execute(BASH_PATH, "mkdir -p #{MAIN_BACKUP_DIR}") | ||
|
||
if device == Yast::BootCommon.mbrDisk | ||
copy_br(device, "/boot/backup_mbr") | ||
# check if file exists | ||
if device_backup_exist? | ||
# move it so we do not overwrite it | ||
change_date = formated_file_ctime(device_file_path) | ||
Yast::SCR.Execute( | ||
BASH_PATH, | ||
Yast::Builtins.sformat("/bin/mv %1 %1-%2", device_file_path, change_date) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
jreidinger
Author
Member
|
||
) | ||
|
||
# save thinkpad MBR | ||
if Yast::BootCommon.ThinkPadMBR(device) | ||
device_file_path_thinkpad = device_file_path + "thinkpadMBR" | ||
log.info("Backup thinkpad MBR") | ||
Yast::SCR.Execute( | ||
BASH_PATH, | ||
"cp #{device_file_path} #{device_file_path_thinkpad}", | ||
) | ||
end | ||
end | ||
cleanup_backups(device_file) | ||
end | ||
|
||
private | ||
# Get last change time of file | ||
# @param [String] filename string name of file | ||
# @return [String] last change date as YYYY-MM-DD-HH-MM-SS | ||
def formated_file_ctime(filename) | ||
stat = Yast::SCR.Read(Yast::Path.new(".target.stat"), filename) | ||
ctime = stat["ctime"] or raise "Cannot get modification time of file #{filename}" | ||
time = DateTime.strptime(ctime.to_s, "%s") | ||
copy_br(device, device_file_path) | ||
|
||
time.strftime("%Y-%m-%d-%H-%M-%S") | ||
end | ||
# save MBR to yast2 log directory | ||
logs_path = "/var/log/YaST2/" + device_file | ||
copy_br(device, logs_path) | ||
|
||
def copy_br(device, target_path) | ||
Yast::SCR.Execute( | ||
BASH_PATH, | ||
"/bin/dd if=#{device} of=#{target_path} bs=512 count=1 2>&1" | ||
) | ||
end | ||
if device == Yast::BootCommon.mbrDisk | ||
copy_br(device, "/boot/backup_mbr") | ||
|
||
def cleanup_backups(device_file) | ||
files = Yast::SCR.Read(Yast::Path.new(".target.dir"), MAIN_BACKUP_DIR) | ||
# clean only backups for this device | ||
files.select! do |c| | ||
c =~ /#{Regexp.escape(device_file)}-\d{4}(?:-\d{2}){5}/ | ||
end | ||
# and sort so we can benefit from its ascending order | ||
files.sort! | ||
files.drop(KEPT_BACKUPS).each do |file_name| | ||
# save thinkpad MBR | ||
if Yast::BootCommon.ThinkPadMBR(device) | ||
device_file_path_thinkpad = device_file_path + "thinkpadMBR" | ||
log.info("Backup thinkpad MBR") | ||
Yast::SCR.Execute( | ||
Yast::Path.new(".target.remove"), | ||
MAIN_BACKUP_DIR + file_name | ||
BASH_PATH, | ||
"cp #{device_file_path} #{device_file_path_thinkpad}", | ||
) | ||
end | ||
end | ||
end | ||
|
||
# Exception used to indicate that backup missing, so any action with it is | ||
# not possible | ||
class Missing < RuntimeError | ||
This comment has been minimized.
Sorry, something went wrong.
ancorgs
Contributor
|
||
def initialize | ||
super "Backup for boot record missing." | ||
end | ||
end | ||
|
||
# Restore backup | ||
# @raise [::Bootloader::BootRecordBackup::Missing] if backup missing | ||
# @return true if copy is successful | ||
def restore | ||
raise Missing.new unless device_backup_exist? | ||
|
||
# Copy only 440 bytes for Vista booting problem bnc #396444 | ||
# and also to not destroy partition table | ||
copy_br(device_file_path, device, bs: 440) == 0 | ||
end | ||
|
||
private | ||
|
||
def device_file | ||
@device_file ||= @device.tr("/", "_") | ||
end | ||
|
||
def device_file_path | ||
@device_file_path ||= MAIN_BACKUP_DIR + device_file | ||
end | ||
|
||
def device_backup_exist? | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
ancorgs
Contributor
|
||
Yast::SCR.Read(TARGET_SIZE, device_file_path) > 0 | ||
end | ||
|
||
# Get last change time of file | ||
# @param [String] filename string name of file | ||
# @return [String] last change date as YYYY-MM-DD-HH-MM-SS | ||
def formated_file_ctime(filename) | ||
stat = Yast::SCR.Read(Yast::Path.new(".target.stat"), filename) | ||
ctime = stat["ctime"] or raise "Cannot get modification time of file #{filename}" | ||
time = DateTime.strptime(ctime.to_s, "%s") | ||
|
||
time.strftime("%Y-%m-%d-%H-%M-%S") | ||
end | ||
|
||
def copy_br(device, target_path, bs: 512) | ||
Yast::SCR.Execute( | ||
BASH_PATH, | ||
"/bin/dd if=#{device} of=#{target_path} bs=#{bs} count=1 2>&1" | ||
) | ||
end | ||
|
||
def cleanup_backups(device_file) | ||
This comment has been minimized.
Sorry, something went wrong.
ancorgs
Contributor
|
||
files = Yast::SCR.Read(Yast::Path.new(".target.dir"), MAIN_BACKUP_DIR) | ||
# clean only backups for this device | ||
files.select! do |c| | ||
c =~ /#{Regexp.escape(device_file)}-\d{4}(?:-\d{2}){5}/ | ||
end | ||
# and sort so we can benefit from its ascending order | ||
files.sort! | ||
files.drop(KEPT_BACKUPS).each do |file_name| | ||
Yast::SCR.Execute( | ||
Yast::Path.new(".target.remove"), | ||
MAIN_BACKUP_DIR + file_name | ||
) | ||
end | ||
end | ||
end | ||
end |
And being extremely nitpicking with the names once again (is the last one, I promise), I'd go for
write
becausecreate
to some extend implies that it didn't existed before, which sometimes is not true (sometimes you are just updating the backup with a new version). An interface with#write
,#restore
and#exists?
(I know is private at the moment) sounds nice to me for aBootRecordBackup
class.