ACC SHELL
/**
* File:
* include/bootloader/grub/misc.ycp
*
* Module:
* Bootloader installation and configuration
*
* Summary:
* Miscelaneous functions for configuring and installing GRUB bootloader
*
* Authors:
* Jiri Srain <jsrain@suse.cz>
* Olaf Dabrunz <od@suse.de>
*
* $Id: misc.ycp 61449 2010-03-23 09:56:57Z juhliarik $
*
*/
{
textdomain "bootloader";
import "Storage";
import "StorageDevices";
import "Mode";
import "BootCommon";
import "PackageSystem";
import "Map";
// --------------------------------------------------------------
// --------------------------------------------------------------
// updateMBR and related stuff, plus InstallingToFloppy() (taken from
// routines/misc.ycp)
/**
* Check if installation to floppy is performed
* @return true if installing bootloader to floppy
*/
global boolean grub_InstallingToFloppy () {
boolean ret = false;
// there is no boot_floppy flag, the installation to floppy devices needs
// to be specified in the boot_custom flag
// (FIXME: which is freely editable, as soon as the generic 'selectdevice'
// widget allows this; also need perl-Bootloader to put the floppy device
// in the list of the boot_custom widget)
if (BootCommon::globals["boot_custom"]:nil == nil)
ret = false;
else if (BootCommon::globals["boot_custom"]:nil ==
StorageDevices::FloppyDevice())
ret = true;
else if (contains (BootStorage::getFloppyDevices (),
BootCommon::globals["boot_custom"]:nil))
ret = true;
y2milestone ("Installing to floppy: %1", ret);
return ret;
}
/**
* Given a device name to which we install the bootloader (loader_device),
* get the name of the partition which should be activated.
* Also return the device file name of the disk device that corresponds to
* loader_device (i.e. where the corresponding MBR can be found).
* @param loader_device string the device to install bootloader to
* @return a map $[ "dev" : string, "mbr": string, "num": any]
* containing device (eg. "/dev/hda4"), disk (eg. "/dev/hda") and
* partition number (eg. 4)
*/
// * @param boot_partition string the partition holding /boot subtree
// map<string,any> getPartitionToActivate (string boot_partition,
// string loader_device)
map<string,any> grub_getPartitionToActivate (string loader_device)
{
map p_dev = Storage::GetDiskPartition (loader_device);
integer num = BootCommon::myToInteger( p_dev["nr"]:nil );
string mbr_dev = p_dev["disk"]:"";
// If loader_device is /dev/md* (which means bootloader is installed to
// /dev/md*), return the info map for the first device in BIOS ID order
// that underlies the soft-RAID and that has a BIOS ID (devices without
// BIOS ID are excluded).
// If no device is found in this way, return the info map for the
// soft-RAID device ("/dev/md", "/dev/md[0-9]*").
// FIXME: use ::storage to detect md devices, not by name!
// FIXME: return info for ALL underlying soft-RAID devices here, so
// that all MBRs can be backed-up and all partitions that need to be
// activated can be activated. This requires a map<map<...>> return
// value, and code on the caller side that evaluates this.
if (substring (loader_device, 0, 7) == "/dev/md")
{
map<string,integer> md = BootCommon::Md2Partitions (loader_device);
integer min = 256; // max. is 255; 256 means "no bios_id found"
string device = "";
foreach (string d, integer id, md, {
if (id < min)
{
min = id;
device = d;
}
});
if (device != "")
{
map p_dev = Storage::GetDiskPartition (device);
num = BootCommon::myToInteger( p_dev["nr"]:nil );
mbr_dev = p_dev["disk"]:"";
}
}
// If loader_device is a disk device ("/dev/sda"), that means that we
// install the bootloader to the MBR. In this case, activate /boot
// partition.
// (partial fix of #20637)
// FIXME: necessity and purpose are unclear: if we install the
// bootloader to the MBR, then why do we need to activate the /boot
// partition? Stage1 of a GRUB has the first block of the stage2
// hard-coded inside.
// This code was added because a /boot partition on a /dev/cciss device
// was not activated in bug #20637. Anyway, it probably never worked,
// since the bootloader was not installed to the MBR in that bug (and
// thus this code is not triggered).
// The real problem may have been that Storage::GetDiskPartition() did
// not know how to parse /dev/cciss/c0d0p1, so that the default case at
// the beginning of this function did not set up correct values. These
// days, Storage::GetDiskPartition() looks OK with /dev/cciss.
// Deactivated this code, so that "/boot" does not get activated
// unecessarily when GRUB stage1 is installed to the MBR anyway (this
// would unecessarily have broken drive C: detection on older MS
// operating systems).
// else if (num == 0)
// {
// p_dev = Storage::GetDiskPartition (boot_partition);
// num = BootCommon::myToInteger( p_dev["nr"]:nil );
// mbr_dev = p_dev["disk"]:"";
//
// if (size (BootCommon::Md2Partitions (boot_partition)) > 1)
// {
// foreach (string k, integer v, BootCommon::Md2Partitions (boot_partition),{
// if (search (k, loader_device) == 0)
// {
// p_dev = Storage::GetDiskPartition (k);
// num = BootCommon::myToInteger( p_dev["nr"]:nil );
// mbr_dev = p_dev["disk"]:"";
// }
// });
// }
// }
// (bnc # 337742) - Unable to boot the openSUSE (32 and 64 bits) after installation
// if loader_device is disk device activate BootStorage::BootPartitionDevice
if (num == 0)
{
y2milestone ("loader_device is disk device");
p_dev = Storage::GetDiskPartition (BootStorage::BootPartitionDevice);
num = BootCommon::myToInteger( p_dev["nr"]:nil );
}
if (num > 4)
{
y2milestone ("Bootloader partition type is logical");
map tm = Storage::GetTargetMap ();
list<map> partitions = tm[mbr_dev, "partitions"]:[];
foreach (map p, partitions, ``{
if (p["type"]:nil == `extended)
{
num = p["nr"]:num;
y2milestone ("Using extended partition %1 instead",num);
}
});
}
map<string,any> ret = $[
"num" : num,
"mbr" : mbr_dev,
"dev" : Storage::GetDeviceName (mbr_dev, num),
];
y2milestone("Partition for activating: %1", ret);
return ret;
}
/**
* Get a list of partitions to activate if user wants to activate
* boot partition
* @return a list of partitions to activate
*/
list<map<string, any> > grub_getPartitionsToActivate () {
map<string,integer> md = $[];
list<list<string> > underlying_devs = [];
list<string> devs = [];
list<string> boot_devices = [];
// bnc#494630 - add also boot partitions from soft-raids
string boot_device = BootCommon::getBootPartition();
if (substring (boot_device, 0, 7) == "/dev/md")
{
boot_devices = add(boot_devices, boot_device);
foreach (string dev, BootCommon::GetBootloaderDevices(), {
boot_devices = add(boot_devices, dev);
});
} else {
boot_devices = BootCommon::GetBootloaderDevices();
}
// get a list of all bootloader devices or their underlying soft-RAID
// devices, if necessary
underlying_devs = maplist (string dev, boot_devices, {
md = BootCommon::Md2Partitions(dev);
if ( size(md) > 0 ) {
devs = maplist (string k, integer v, md, ``(k));
return devs;
}
return [dev];
});
list<string> bootloader_base_devices = flatten(underlying_devs);
if (size (bootloader_base_devices) == 0)
{
bootloader_base_devices = BootCommon::GetBootloaderDevices();
}
list<map<string,any> > ret = maplist (string partition, bootloader_base_devices, {
return grub_getPartitionToActivate (partition);
});
return toset (ret);
}
/**
* Get the list of MBR disks that should be rewritten by generic code
* if user wants to do so
* @return a list of device names to be rewritten
*/
list<string> grub_getMbrsToRewrite () {
list<string> ret = [BootCommon::mbrDisk];
map<string,integer> md = $[];
list<list<string> > underlying_devs = [];
list<string> devs = [];
list<string> boot_devices = [];
// bnc#494630 - add also boot partitions from soft-raids
string boot_device = BootCommon::getBootPartition();
if (substring (boot_device, 0, 7) == "/dev/md")
{
boot_devices = add(boot_devices, boot_device);
foreach (string dev, BootCommon::GetBootloaderDevices(), {
boot_devices = add(boot_devices, dev);
});
} else {
boot_devices = BootCommon::GetBootloaderDevices();
}
// get a list of all bootloader devices or their underlying soft-RAID
// devices, if necessary
underlying_devs = maplist (string dev, boot_devices, {
md = BootCommon::Md2Partitions(dev);
if ( size(md) > 0 ) {
devs = maplist (string k, integer v, md, ``(k));
return devs;
}
return [dev];
});
list<string> bootloader_base_devices = flatten(underlying_devs);
// find the MBRs on the same disks as the devices underlying the boot
// devices; if for any of the "underlying" or "base" devices no device
// for acessing the MBR can be determined, include mbrDisk in the list
list<string> mbrs = maplist (string dev, bootloader_base_devices, {
dev = (grub_getPartitionToActivate (dev))["mbr"]:BootCommon::mbrDisk;
return dev;
});
// FIXME: the exact semantics of this check is unclear; but it seems OK
// to keep this as a sanity check and a check for an empty list;
// mbrDisk _should_ be included in mbrs; the exact cases for this need
// to be found and documented though
if (contains (mbrs, BootCommon::mbrDisk))
{
ret = (list<string>)merge (ret, mbrs);
}
return toset (ret);
}
/**
* Get last change time of file
* @param filename string name of file
* @return string last change date as YYYY-MM-DD-HH-MM-SS
*/
string grub_getFileChangeDate (string filename) {
map stat = (map) SCR::Read (.target.stat, filename);
integer ctime = stat["ctime"]:0;
string command = sformat (
"date --date='1970-01-01 00:00:00 %1 seconds' +\"%%Y-%%m-%%d-%%H-%%M-%%S\"",
ctime);
map out = (map) SCR::Execute (.target.bash_output, command);
string c_time = out["stdout"]:"";
y2debug ("File %1: last change %2", filename, c_time);
return c_time;
}
/**
* Save current MBR to /boot/backup_mbr
* Also save to /var/lib/YaST2/backup_boot_sectors/%device, if some
* existing, rename it
* @param device string name of device
*/
void grub_saveMBR (string device) {
string device_file = mergestring (splitstring (device, "/"), "_");
string device_file_path = "/var/lib/YaST2/backup_boot_sectors/"
+ device_file;
string device_file_path_to_logs = "/var/log/YaST2/"
+ device_file;
SCR::Execute (.target.bash,
"test -d /var/lib/YaST2/backup_boot_sectors || mkdir /var/lib/YaST2/backup_boot_sectors");
if (SCR::Read (.target.size, device_file_path) > 0)
{
list<string> contents = (list<string>) SCR::Read (.target.dir, "/var/lib/YaST2/backup_boot_sectors");
contents = filter (string c, contents, ``(regexpmatch (c, sformat (
"%1-.*-.*-.*-.*-.*-.*", device_file))));
contents = sort (contents);
integer index = 0;
integer siz = size (contents);
while (index + 10 < siz)
{
SCR::Execute (.target.remove,
sformat ("/var/lib/YaST2/backup_boot_sectors/%1", contents[index]:""));
index = index + 1;
}
string change_date = grub_getFileChangeDate (device_file_path);
SCR::Execute (.target.bash, sformat (
"/bin/mv %1 %1-%2",
device_file_path, change_date));
}
SCR::Execute (.target.bash, sformat (
"/bin/dd if=%1 of=%2 bs=512 count=1 2>&1",
device, device_file_path));
// save MBR to yast2 log directory
SCR::Execute (.target.bash, sformat (
"/bin/dd if=%1 of=%2 bs=512 count=1 2>&1",
device, device_file_path_to_logs));
if (device == BootCommon::mbrDisk)
{
SCR::Execute (.target.bash, sformat (
"/bin/dd if=%1 of=%2 bs=512 count=1 2>&1",
device, "/boot/backup_mbr"));
// save thinkpad MBR
if (BootCommon::ThinkPadMBR(device))
{
string device_file_path_thinkpad = device_file_path + "thinkpadMBR";
y2milestone("Backup thinkpad MBR");
SCR::Execute(.target.bash, sformat (
"cp %1 %2 2>&1", device_file_path, device_file_path_thinkpad));
}
}
}
/**
* Update contents of MBR (active partition and booting code)
* FIXME move tis function to lilolike.ycp
* @return boolean true on success
*/
global define boolean grub_updateMBR () ``{
// FIXME: do the real thing in perl_Bootloader
boolean activate = ( BootCommon::globals["activate"]:"false" == "true" );
boolean generic_mbr = ( BootCommon::globals["generic_mbr"]:"false" == "true" );
y2milestone ("Updating disk system area, activate partition: %1, " +
"install generic boot code in MBR: %2", activate, generic_mbr);
// After a proposal is done, Bootloader::Propose() always sets
// backup_mbr to true. The default is false. No other parts of the code
// currently change this flag.
if (BootCommon::backup_mbr)
{
y2milestone ("Doing MBR backup: MBR Disk: %1, loader devices: %2",
BootCommon::mbrDisk, BootCommon::GetBootloaderDevices());
list<string> disks_to_rewrite = (list<string>)toset (merge (
grub_getMbrsToRewrite (),
merge([BootCommon::mbrDisk], BootCommon::GetBootloaderDevices())));
y2milestone ("Creating backup of boot sectors of %1",
disks_to_rewrite);
foreach (string d, disks_to_rewrite, {
grub_saveMBR (d);
});
}
boolean ret = true;
// if the bootloader stage 1 is not installed in the MBR, but
// ConfigureLocation() asked us to replace some problematic existing
// MBR, then overwrite the boot code (only, not the partition list!) in
// the MBR with generic (currently DOS?) bootloader stage1 code
if (generic_mbr && ! contains(BootCommon::GetBootloaderDevices(), BootCommon::mbrDisk))
{
if (! Stage::initial ())
{
PackageSystem::Install ("master-boot-code");
}
y2milestone("Updating code in MBR: MBR Disk: %1, loader devices: %2",
BootCommon::mbrDisk, BootCommon::GetBootloaderDevices());
list<string> disks_to_rewrite = grub_getMbrsToRewrite ();
foreach (string d, disks_to_rewrite, {
y2milestone ("Copying generic MBR code to %1", d);
// added fix 446 -> 440 for Vista booting problem bnc #396444
string command = sformat (
"/bin/dd bs=440 count=1 if=%1 of=%2",
"/usr/lib/boot/master-boot-code",
d);
y2milestone ("Running command %1", command);
map out = (map)SCR::Execute (.target.bash_output, command);
integer exit = out["exit"]:0;
y2milestone ("Command output: %1", out);
ret = ret && (0 == exit);
});
}
if (activate)
{
foreach (map m_activate, grub_getPartitionsToActivate (), {
any num = m_activate["num"]:0;
string mbr_dev = m_activate["mbr"]:"";
if (num != 0 && mbr_dev != "")
{
// if primary partition
if ((! is (num, integer)) || num <= 4)
{
y2milestone ("Activating partition %1 on %2", num, mbr_dev);
// FIXME: this is the most rotten code since molded sliced bread
// move to bootloader/Core/GRUB.pm or similar
// TESTME: make sure that parted does not destroy BSD
// slices (#suse24740): cf. section 5.1 of "info parted":
// Parted only supports the BSD disk label system.
// Parted is unlikely to support the partition slice
// system in the future because the semantics are rather
// strange, and don't work like "normal" partition tables
// do.
// FIXED: investigate proper handling of the activate flag
// (kernel ioctls in parted etc.) and fix parted
string command = sformat
("/usr/sbin/parted -s %1 set %2 boot on", mbr_dev, num);
y2milestone ("Running command %1", command);
map out = (map)SCR::Execute (.target.bash_output, command);
integer exit = out["exit"]:0;
y2milestone ("Command output: %1", out);
ret = ret && (0 == exit);
}
}
else
{
y2error ("Cannot activate %1", m_activate);
}
});
}
return ret;
}
// --------------------------------------------------------------
// --------------------------------------------------------------
// LocationProposal() and related stuff (taken from routines/lilolike.ycp)
/**
* SetBootloaderDevice()
* Set "boot_*" flags in the globals map according to the boot device selected
* with parameter selected_location. Only a single boot device can be selected
* with this function. The function cannot be used to set a custom boot device.
* It will always be deleted.
*
* FIXME: `mbr_md is probably unneeded; AFA we can see, this decision is
* automatic anyway and perl-Bootloader should be able to make it without help
* from the user or the proposal.
*
* @param selected_location symbol one of `boot `root `mbr `extended `mbr_md `none
*/
define void SetBootloaderDevice(symbol selected_location) {
// first, default to all off:
foreach (string flag, ["boot_boot", "boot_root", "boot_mbr", "boot_extended"], {
BootCommon::globals[flag] = "false";
});
// need to remove the boot_custom key to switch this value off
if (haskey (BootCommon::globals, "boot_custom")) {
BootCommon::globals = remove (BootCommon::globals, "boot_custom");
}
if ( selected_location == `root ) {
BootCommon::globals["boot_root"] = "true";
} else if ( selected_location == `boot ) {
BootCommon::globals["boot_boot"] = "true";
} else if ( selected_location == `mbr ) {
BootCommon::globals["boot_mbr"] = "true";
} else if ( selected_location == `extended ) {
BootCommon::globals["boot_extended"] = "true";
}
}
/**
* function check all partitions and it tries to find /boot partition
* if it is MD Raid and soft-riad return correct device for analyse MBR
* @param list<map> list of partitions
* @return string device for analyse MBR
*/
define string soft_MDraid_boot_disk(list<map> partitions)
{
string result = "";
string boot_device = "";
if ((BootStorage::BootPartitionDevice != nil) && (BootStorage::BootPartitionDevice != ""))
boot_device = BootStorage::BootPartitionDevice;
else
boot_device = BootStorage::RootPartitionDevice;
foreach(map p, partitions, {
if (p["device"]:"" == boot_device)
{
if ((p["type"]:nil == `sw_raid) && (tolower(p["fstype"]:"") == "md raid"))
{
string device_1 = p["devices",0]:"";
y2debug("device_1: %1", device_1);
map dp = Storage::GetDiskPartition (device_1);
y2debug("dp: %1", dp);
result = dp["disk"]:"";
}
}
});
y2milestone("Device for analyse MBR from soft-raid (MD-Raid only): %1", result);
return result;
}
/**
* grub_ConfigureLocation()
* Where to install the bootloader.
* Returns the type of device where to install: one of `boot `root `mbr `extended `mbr_md
* Also sets the boot_* keys in the internal global variable globals accordingly.
*
* @return string type of location proposed to bootloader
*/
define symbol grub_ConfigureLocation() {
// NOTE: selected_location is a temporary local variable now; the global
// variable is not used for grub anymore
symbol selected_location = `mbr; // default to mbr
boolean vista_mbr = false;
// check whether the /boot partition
// - is primary: is_logical -> false
// - is on the first disk (with the MBR): boot_partition_is_on_mbr_disk -> true
map<string,any> tm = Storage::GetTargetMap ();
map dp = Storage::GetDiskPartition (BootStorage::BootPartitionDevice);
string boot_partition_disk = dp["disk"]:"";
boolean boot_partition_is_on_mbr_disk =
(boot_partition_disk == BootCommon::mbrDisk);
map dm = tm[boot_partition_disk]:$[];
list<map> partitions_on_boot_partition_disk = dm["partitions"]:[];
boolean is_logical = false;
string extended = nil;
// determine the underlying devices for the "/boot" partition (either the
// BootPartitionDevice, or the devices from which the soft-RAID device for
// "/boot" is built)
list<string> underlying_boot_partition_devices = [ BootStorage::BootPartitionDevice ];
map<string,integer> md_info = BootCommon::Md2Partitions (BootStorage::BootPartitionDevice);
if (md_info != nil && size (md_info) > 0)
{
boot_partition_is_on_mbr_disk = false;
underlying_boot_partition_devices = maplist (string dev, integer bios_id, md_info, {
map pdp = Storage::GetDiskPartition (dev);
string p_disk = pdp["disk"]:"";
if (p_disk == BootCommon::mbrDisk)
boot_partition_is_on_mbr_disk = true;
return dev;
});
}
y2milestone ("Boot partition devices: %1", underlying_boot_partition_devices);
foreach (map p, partitions_on_boot_partition_disk, {
if (p["type"]:nil == `extended)
{
extended = (string)p["device"]:nil;
}
else if (contains (underlying_boot_partition_devices, p["device"]:"")
&& p["type"]:nil == `logical)
{
// If any of the underlying_boot_partition_devices can be found on
// the boot_partition_disk AND is a logical partition, set
// is_logical to true.
// For soft-RAID this will not match anyway ("/dev/[hs]da*" does not
// match "/dev/md*").
is_logical = true;
}
});
y2milestone ("/boot is on 1st disk: %1", boot_partition_is_on_mbr_disk);
y2milestone ("/boot is in logical partition: %1", is_logical);
y2milestone ("The extended partition: %1", extended);
// keep_mbr, if the MBR contains special code that needs to be kept,
// like Thinkpad boot code (and ATM only Thinkpad boot code
// is recognized)
boolean keep_mbr = BootCommon::KeepMBR (BootCommon::mbrDisk);
// if is primary, store bootloader there
integer exit = 0;
// there was check if boot device is on logical partition
// IMO it is good idea check MBR also in this case
// see bug #279837 comment #53
if (boot_partition_is_on_mbr_disk)
{
selected_location = (BootStorage::BootPartitionDevice != BootStorage::RootPartitionDevice) ? `boot : `root;
BootCommon::globals["activate"] = "true";
BootCommon::activate_changed = true;
// check if there is raid and if it soft-raid select correct device for analyse MBR
// bnc #398356
if (size (underlying_boot_partition_devices) > 1)
boot_partition_disk = soft_MDraid_boot_disk(partitions_on_boot_partition_disk);
if (boot_partition_disk == "")
boot_partition_disk = dp["disk"]:"";
// bnc #483797 cannot read 512 bytes from...
string out = "";
if (boot_partition_disk != "")
{
out = BootCommon::examineMBR(boot_partition_disk);
} else {
y2error("Boot partition disk not found");
}
BootCommon::globals["generic_mbr"] = ((out != "vista") && (! keep_mbr)) ? "true" : "false";
if (out == "vista")
{
y2milestone("Vista MBR...");
vista_mbr = true;
}
}
else if (size (underlying_boot_partition_devices) > 1)
{
// FIXME: `mbr_md is probably unneeded; AFA we can see, this decision is
// automatic anyway and perl-Bootloader should be able to make it without help
// from the user or the proposal.
// In one or two places yast2-bootloader needs to find out all underlying MBR
// devices, if we install stage 1 to a soft-RAID. These places need to find out
// themselves if we have MBRs on a soft-RAID or not.
// selected_location = `mbr_md;
selected_location = `mbr;
}
if (keep_mbr)
{
if (is_logical && extended != nil)
selected_location = `extended;
else
selected_location = (BootStorage::BootPartitionDevice != BootStorage::RootPartitionDevice) ? `boot : `root;
}
SetBootloaderDevice(selected_location);
if (! contains (BootStorage::getPartitionList (`boot, "grub"), (BootCommon::GetBootloaderDevices())[0]:nil))
{
selected_location = `mbr; // default to mbr
SetBootloaderDevice(selected_location);
}
y2milestone ("grub_ConfigureLocation (%1 on %2)",
selected_location, BootCommon::GetBootloaderDevices());
// set active flag, if needed
if (selected_location == `mbr && size (underlying_boot_partition_devices) <= 1)
{
// We are installing into MBR:
// If there is an active partition, then we do not need to activate
// one (otherwise we do).
// Reason: if we use our own MBR code, we do not rely on the activate
// flag in the partition table to boot Linux. Thus, the activated
// partition can remain activated, which causes less problems with
// other installed OSes like Windows (older versions assign the C:
// drive letter to the activated partition).
BootCommon::globals["activate"] =
(size (Storage::GetBootPartition (BootCommon::mbrDisk)) == 0) ? "true" : "false";
}
else
{
// if not installing to MBR, always activate (so the generic MBR will
// boot Linux)
// kokso: fix the problem with proposing installation generic boot code to "/" or "/boot"
// kokso: if boot device is on logical partition
if (is_logical && extended != nil && (BootCommon::globals["generic_mbr"]:"" == "true" ||
vista_mbr))
selected_location = `extended;
BootCommon::globals["activate"] = "true";
SetBootloaderDevice(selected_location);
}
return selected_location;
}
/**
* Find extended partition device (if it exists) on the same device where the
* BootPartitionDevice is located
*
* BootPartitionDevice must be set
*
* @return string device name of extended partition, or nil if none found
*/
string grub_GetExtendedPartitionDev () {
string ret = nil;
map<string,any> tm = Storage::GetTargetMap ();
string device = "";
if (BootStorage::BootPartitionDevice != "")
device = BootStorage::BootPartitionDevice;
else
device = BootStorage::RootPartitionDevice;
map dp = Storage::GetDiskPartition (device);
string disk = dp["disk"]:"";
map dm = tm[disk]:$[];
list<map> partitions = dm["partitions"]:[];
foreach (map p, partitions, {
if (p["type"]:nil == `extended)
{
ret = (string)p["device"]:nil;
}
});
return ret;
}
/**
* Detect "/boot", "/" (root), extended partition device and MBR disk device
*
* If no bootloader device has been set up yet (globals["boot_*"]), or the
* first (FIXME(!)) device is not available as a boot partition, also call
* grub_ConfigureLocation to configure globals["boot_*"] and set the
* globals["activate"] and globals["generic_mbr"] flags if needed
* all these settings are stored in internal variables
*/
define void grub_DetectDisks () {
// #151501: AutoYaST also needs to know the activate flag and the
// "boot_*" settings (formerly the loader_device); jsrain also said
// that skipping setting these variables is probably a bug:
// commenting out the skip code, but this may need to be changed and made dependent
// on a "clone" flag (i.e. make the choice to provide minimal (i.e. let
// YaST do partial proposals on the target system) or maximal (i.e.
// stay as closely as possible to this system) info in the AutoYaST XML
// file)
// if (Mode::config ())
// return;
map mp = Storage::GetMountPoints();
list mountdata_boot = mp["/boot"]:(mp["/"]:[]);
list mountdata_root = mp["/"]:[];
y2milestone( "mountPoints %1", mp );
y2milestone( "mountdata_boot %1", mountdata_boot );
BootStorage::RootPartitionDevice = mp["/", 0]:"";
if (BootStorage::RootPartitionDevice == "")
{
y2error ("No mountpoint for / !!");
}
// if /boot changed, re-configure location
BootStorage::BootPartitionDevice
= mountdata_boot[0]:BootStorage::RootPartitionDevice;
// get extended partition device (if exists)
BootStorage::ExtendedPartitionDevice = grub_GetExtendedPartitionDev();
if (BootCommon::mbrDisk == "" || BootCommon::mbrDisk == nil)
{
// mbr detection.
BootCommon::mbrDisk = BootCommon::FindMBRDisk();
}
// if no bootloader devices have been set up, or any of the set up
// bootloader devices have become unavailable, then re-propose the
// bootloader location.
list<string> all_boot_partitions = BootStorage::getPartitionList(`boot, "grub");
list<string> bldevs = BootCommon::GetBootloaderDevices();
boolean need_location_reconfigure = false;
if ( bldevs == nil || bldevs == ["/dev/null"] )
need_location_reconfigure = true;
else {
foreach (string dev, bldevs, {
if (!contains(all_boot_partitions, dev))
need_location_reconfigure = true;
});
}
if ( need_location_reconfigure )
grub_ConfigureLocation ();
}
/**
* Check whether any disk settings for the disks we currently use were changed
* since last checking
* @return map map containing boolean "changed" and string "reason"
*/
define map<string, any> grub_DisksChanged () {
map<string, any> ret = $[
"changed" : false,
"reason" : ""
];
if (Mode::config ())
return ret;
map mp = Storage::GetMountPoints();
string actual_root = mp["/", 0]:"";
string actual_boot = mp["/boot", 0]:actual_root;
string actual_extended = grub_GetExtendedPartitionDev();
if ( BootCommon::globals["boot_boot"]:"false" == "true" &&
actual_boot != BootStorage::BootPartitionDevice )
ret = $[
"changed" : true,
"reason" : (string) ret["reason"]:"" +
"Selected bootloader location \"/boot\" is not on " +
BootStorage::BootPartitionDevice +
" any more.\n"
];
if ( BootCommon::globals["boot_root"]:"false" == "true" &&
actual_root != BootStorage::RootPartitionDevice )
ret = $[
"changed" : true,
"reason" : (string) ret["reason"]:"" +
"Selected bootloader location \"/\" is not on " +
BootStorage::RootPartitionDevice +
" any more.\n"
];
if ( BootCommon::globals["boot_mbr"]:"false" == "true" ) {
string actual_mbr = BootCommon::FindMBRDisk();
if ( actual_mbr != BootCommon::mbrDisk )
ret = $[
"changed" : true,
"reason" : (string) ret["reason"]:"" +
"Selected bootloader location MBR is not on " +
BootCommon::mbrDisk +
" any more.\n"
];
}
if ( BootCommon::globals["boot_extended"]:"false" == "true" &&
actual_extended != BootStorage::ExtendedPartitionDevice )
ret = $[
"changed" : true,
"reason" : (string) ret["reason"]:"" +
"Selected bootloader location \"extended partition\" is not on " +
BootStorage::ExtendedPartitionDevice +
" any more.\n"
];
if ( BootCommon::globals["boot_custom"]:nil != nil && BootCommon::globals["boot_custom"]:nil != "") {
list all_boot_partitions = BootStorage::getPartitionList(`boot, "grub");
if ( ! contains(all_boot_partitions, BootCommon::globals["boot_custom"]:nil) )
ret = $[
"changed" : true,
"reason" : (string) ret["reason"]:"" +
"Selected custom bootloader partition " +
BootCommon::globals["boot_custom"]:nil +
" is not available any more.\n"
];
}
if ( ret["changed"]:false )
y2milestone ("Location should be set again");
return ret;
}
/**
* Propose the boot loader location for grub
* - if no proposal has been made, collects the devices for "/", "/boot", MBR
* and makes a new proposal
* - if no device mapping exists, creates a device mapping
* - if the devices that were somehow (proposal, user interface) selected for
* bootloader installation do not match the current partitioning any more
* (e.g. "/boot" partition was selected but is not available anymore (and
* "/" did not move there), "/" was selected but has moved, etc.), then also
* re-collect the devices for "/", "/boot", MBR and make a new proposal
*/
define void grub_LocationProposal () {
y2milestone("globals: %1", BootCommon::globals);
y2milestone("Mode::autoinst: %1", Mode::autoinst());
y2milestone("haskey( BootCommon::globals, boot_boot ): %1", haskey( BootCommon::globals, "boot_boot" ));
string md_mbr = "";
if (! BootCommon::was_proposed ||
// During autoinstall, the autoyast profile must contain a bootloader
// device specification (we currently really only support system
// *cloning* with autoyast...). But as a convenience, and because
// this kind of magic is available for empty globals and sections, we
// propose a bootloader location if none was specified.
// Note that "no bootloader device" can be specified by explicitly
// setting this up, e.g. by setting one or all boot_* flags to
// "false".
// FIXME: add to LILO, ELILO; POWERLILO already should have this
// (check again)
( Mode::autoinst() &&
! haskey( BootCommon::globals, "boot_boot" ) &&
! haskey( BootCommon::globals, "boot_root" ) &&
! haskey( BootCommon::globals, "boot_mbr" ) &&
! haskey( BootCommon::globals, "boot_extended" ) &&
// ! haskey( BootCommon::globals, "boot_mbr_md" ) &&
! haskey( BootCommon::globals, "boot_custom" )
))
{
grub_DetectDisks ();
BootCommon::del_parts = BootStorage::getPartitionList (`deleted, "grub");
// check whether edd is loaded; if not: load it
string lsmod_command = "lsmod | grep edd";
y2milestone ("Running command %1", lsmod_command);
map lsmod_out = (map)SCR::Execute (.target.bash_output, lsmod_command);
y2milestone ("Command output: %1", lsmod_out);
boolean edd_loaded = lsmod_out["exit"]:0 == 0;
if (! edd_loaded)
{
string command = "/sbin/modprobe edd";
y2milestone ("Loading EDD module, running %1", command);
map out = (map)SCR::Execute (.target.bash_output, command);
y2milestone ("Command output: %1", out);
}
md_mbr = BootStorage::addMDSettingsToGlobals();
if (md_mbr != "")
BootCommon::globals["boot_md_mbr"] = md_mbr;
}
y2milestone("(2) globals: %1", BootCommon::globals);
// refresh device map
if ((BootStorage::device_mapping == nil)
|| (size (BootStorage::device_mapping) == 0)
|| ((BootCommon::cached_settings_base_data_change_time != Storage::GetTargetChangeTime()) &&
// bnc#585824 - Bootloader doesn't use defined device map from autoyast
(!(Mode::autoinst() && (BootCommon::cached_settings_base_data_change_time == nil)))))
{
BootStorage::ProposeDeviceMap();
md_mbr = BootStorage::addMDSettingsToGlobals();
if (md_mbr != "")
BootCommon::globals["boot_md_mbr"] = md_mbr;
BootCommon::InitializeLibrary (true, "grub");
}
if ( ! Mode::autoinst () ) {
map<string, any> changed = grub_DisksChanged ();
if ( changed["changed"]:false ) {
if (BootCommon::askLocationResetPopup (changed["reason"]:"Disk configuration changed.\n")) {
SetBootloaderDevice(`none);
y2milestone ("Reconfiguring locations");
grub_DetectDisks ();
}
}
}
}
// --------------------------------------------------------------
// --------------------------------------------------------------
// other stuff
/** FATE #301994 - Correct device mapping in case windows is installed on the second HD
* Check if chainloader section with windows is on the first disk
*
* @param map<string,any> section from BootCommon::sections
* @return boolean true if it is necessary remap section
*/
define boolean isWidnowsOnBootDisk(map<string,any> section)
{
// check if it is windows chainloader
if ((search(tolower(section["name"]:""), "windows") != nil)
|| (search(tolower(section["original_name"]:""), "windows") != nil))
{
map p_dev = Storage::GetDiskPartition (section["chainloader"]:"");
string disk_dev = p_dev["disk"]:"";
if (disk_dev == "")
{
y2error("trying find disk for windows chainloader failed");
return false;
}
// find grub id in device map for chainloader device
string grub_id = BootStorage::device_mapping[disk_dev]:"";
y2milestone("Disk from windows chainloader: %1 grub id from device map: %2", disk_dev, grub_id);
// check if disk is the first in order...
if (grub_id != "hd0")
return true;
}
return false;
}
/** FATE #301994 - Correct device mapping in case windows is installed on the second HD
* Remap and make active windows chainloader section
* if it is not on the boot (the first) disk
* @param list of sections
* @return list of sections
*/
define list<map<string,any> > checkWindowsSection(list<map<string,any> > sections)
{
// list of idexes from sections where is chainloader
// and where is necessary add remapping and makeactive
list < integer > list_index =[];
// counter
integer index = -1;
// check all sections...
foreach(map<string,any> section, sections,
{
index = index +1;
if (haskey(section, "chainloader"))
{
y2debug("chainloader section: %1", section);
// add only indexes for update
if (isWidnowsOnBootDisk(section))
list_index = add(list_index, index);
};
});
if (size(list_index) > 0)
{
foreach (integer idx, list_index,
{
sections[idx, "remap"] = "true";
sections[idx, "makeactive"] = "true";
y2milestone("Added remap and makeactive for section: %1", sections[idx]:$[]);
});
}
y2debug("Checking sections for windows chainloader: %1", sections);
return sections;
}
/**
* FATE #303548 - Grub: limit device.map to devices detected by BIOS Int 13
* The function reduces records (devices) in device.map
* Grub doesn't support more than 8 devices in device.map
* @return boolean true if device map was reduced
*/
global boolean ReduceDeviceMapTo8()
{
boolean result = false;
if (size(BootStorage::device_mapping)>8)
{
result = true;
list <string> bios_order = (list <string> ) Map::Values(BootStorage::device_mapping);
//delete all grub devices with order more than 9
bios_order = filter(string key, bios_order, {return (size(key) < 4);});
bios_order = lsort(bios_order);
y2debug("ordered values (grub devices): %1", bios_order);
map<string,string> inverse_device_map = $[];
map<string,string> new_device_map = $[];
y2milestone("Device map before reducing: %1", BootStorage::device_mapping);
foreach(string key, string value, BootStorage::device_mapping,
{
inverse_device_map[value]=key;
});
y2debug("inverse_device_map: %1", inverse_device_map);
integer index = 0;
foreach(string key, bios_order,
{
string device_name = inverse_device_map[key]:"";
if (index < 8)
{
y2debug("adding device: %1 with key: %2 and index is: %3", device_name, key, index);
new_device_map[device_name]=key;
index = index + 1;
} else {
break;
}
});
BootStorage::device_mapping = new_device_map;
y2milestone("Device map after reducing: %1", BootStorage::device_mapping);
} else {
y2milestone("Device map includes less than 9 devices. It is not reduced. device_map: %1", BootStorage::device_mapping);
}
return result;
}
/**
* FATE #303548 - Grub: limit device.map to devices detected by BIOS Int 13
* The function check if boot device is in device.map
* Grub doesn't support more than 8 devices in device.map
* @param string boot device
* @param string boot device with name by mountby
* @return boolean true if there is missing boot device
*/
define boolean checkBootDeviceInDeviceMap(string boot_dev, string boot_dev_mountby)
{
boolean result = false;
if (size(BootStorage::device_mapping)>8)
{
result = false;
list <string> bios_order = (list <string> ) Map::Values(BootStorage::device_mapping);
//delete all grub devices with order more than 9
bios_order = filter(string key, bios_order, {return (size(key) < 4);});
bios_order = lsort(bios_order);
y2debug("ordered values (grub devices): %1", bios_order);
map<string,string> inverse_device_map = $[];
foreach(string key, string value, BootStorage::device_mapping,
{
inverse_device_map[value]=key;
});
y2debug("inverse_device_map: %1", inverse_device_map);
integer index = 0;
boolean boot_device_added = false;
foreach(string key, bios_order,
{
string device_name = inverse_device_map[key]:"";
if (index < 8)
{
if ((device_name == boot_dev) || (device_name == boot_dev_mountby))
boot_device_added = true;
index = index + 1;
} else {
if (boot_device_added)
{
y2milestone("Device map includes boot disk");
break;
} else {
y2error("Device map doesn't include boot disk");
result = true;
break;
}
}
});
} else {
y2milestone("Device map includes less than 9 devices.");
}
return result;
}
} //last "}"
/*
* Local variables:
* mode: ycp
* mode: font-lock
* mode: auto-fill
* indent-level: 4
* fill-column: 78
* End:
*/
ACC SHELL 2018