ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/RootPart.ycp

/**
 * Module:	RootPart.ycp
 *
 * Authors:	Arvin Schnell <arvin@suse.de>
 *
 * Purpose:	Responsible for searching of root partitions and
 *		mounting of target partitions.
 *
 * $Id: RootPart.ycp 61748 2010-04-19 10:27:58Z aschnell $
 */
{
    module "RootPart";

    textdomain "update";

    import "Directory";
    import "Mode";
    import "Linuxrc";
    import "Storage";
    import "Popup";
    import "ModuleLoading";
    import "FileSystems";
    import "Update";
    import "SuSERelease";
    import "FileUtils";
    import "Arch";
    import "String";
    import "Installation";
    import "Report";
    import "Label";
    import "Stage";
    import "Wizard";

    include "partitioning/custom_part_dialogs.ycp";


    // Selected root partition for the update or boot.
    global string selectedRootPartition = "";

    // FATE #301844, to find out that a system for update has been changed
    global string previousRootPartition = "";

    // Map of all root partitions (key) and information map (value).
    // The information map contains the keys `valid, `name and `arch.
    global map<string,map> rootPartitions = $[];

    // Number of valid root partitions.
    global integer numberOfValidRootPartitions = 0;

    // Show all partitions (not only root partitions) in the dialog.
    global boolean showAllPartitions = false;

    // Did we search for root partitions
    global boolean didSearchForRootPartitions = false;

    // We successfully mounted the target partitions
    global boolean targetOk = false;

    // Did we try to mount the target partitions?
    global boolean did_try_mount_partitions = false;
    
    list <string> already_checked_jfs_partitions = [];

    list <string> non_modular_fs = [ "proc", "sysfs" ];

    /**
     * List of mounted partitions, activated swap partitions and loop devices.
     * Amongst other things used for reversing action if mode is changed from
     * update to new installation or if root partition for update is changed.
     * The order of the list if of paramount importance.
     *
     * Each item is list [string value, string type [, string device]] where:
     *
     * Keys/values are:
     *
     *   `type     The type, one of "mount", "swap" or "crypt".
     *
     *   `device   The device.
     *
     *   `mntpt    The mount point, only for `type = "mount".  Does not
     *             include Installation::destdir.
     */
    list <map <symbol, string> > activated = [];

    /**
     * Returns currently activated partitions.
     *
     * @return list <map <symbol, string> > activated
     */
    global list <map <symbol, string> > GetActivated () {
	return activated;
    }

    global boolean Mounted () {
	return size (activated) > 0;
    }


    /**
     *  Link to SDB article concerning renaming of devices.
     */
    string sdb = sformat (_("See the SDB article at %1 for details
about how to solve this problem."),
"http://support.novell.com/techcenter/sdb/en/2003/03/fhassel_update_not_possible.html");


    /**
     * Get the key what of the selected root partition.
     */
    global define string GetInfoOfSelected (symbol what)
    {
	map i = rootPartitions[selectedRootPartition]:$[];

	if (what == `name) {
	    // Name is known
	    if (i[what]:"" != "") {
		return i[what]:"";

	    // Linux partition, but no root FS found
	    } else if (contains(FileSystems::possible_root_fs, i[`fs]:`nil)) {
		// label - name of sustem to update
		return _("Unknown Linux System");

	    // Non-Linux
	    } else {
		// label - name of sustem to update
		return _("Non-Linux System");
	    }
	} else {
	    // label - name of sustem to update
	    return i[what]:_("Unknown");
	}
    }


    /**
     * Set the selected root partition to some valid one. Only
     * make sense if the number of valid root partition is one.
     */
    global define void SetSelectedToValid ()
    {
	selectedRootPartition = "";
	foreach (string p, map i, rootPartitions, {
	    if (i[`valid]:false && selectedRootPartition == "")
		selectedRootPartition = p;
	});
    }


    /**
     *
     */
    define void AddToTargetMap ()
    {
	map <string, map> target_map = (map <string, map>) Storage::GetOndiskTarget ();
	y2milestone ("On disk target map: %1", target_map);
	list <map> tmp = filter (map e, activated, ``(e[`type]:"" == "mount"));
	foreach (map e, tmp, {
	    y2milestone ("Setting partition data: Device: %1, MountPoint: %2", e[`device]:"", e[`mntpt]:"");
	    target_map = Storage::SetPartitionData (target_map, e[`device]:"", "mount", e[`mntpt]:"");
            if( issubstring( e[`device]:"", "/dev/disk/by-id" ) )
                target_map = Storage::SetPartitionData (target_map, e[`device]:"", "mountby", `id);
            else if( issubstring( e[`device]:"", "/dev/" ) )
                target_map = Storage::SetPartitionData (target_map, e[`device]:"", "mountby", `device);
            else
                target_map = Storage::SetPartitionData (target_map, e[`device]:"", "mountby", `label);
	});
	tmp = filter (map e, activated, ``(e[`type]:"" == "swap"));
	foreach (map e, tmp, {
	    y2milestone ("Setting swap partition data: Device: %1",
		e[`device]:"");
	    target_map = Storage::SetPartitionData (target_map, e[`device]:"",
						    "mount", "swap");
            if( issubstring( e[`device]:"", "/dev/disk/by-id" ) )
                target_map = Storage::SetPartitionData (target_map, e[`device]:"", "mountby", `id);
            else if( issubstring( e[`device]:"", "/dev/" ) )
                target_map = Storage::SetPartitionData (target_map, e[`device]:"", "mountby", `device);
            else
                target_map = Storage::SetPartitionData (target_map, e[`device]:"", "mountby", `label);
	});
	y2milestone ("Setting target map: %1", target_map);
	Storage::SetTargetMap (target_map);
    }


    /**
     *
     */
    define void RemoveFromTargetMap ()
    {
	map <string, map> target_map = Storage::GetTargetMap ();
	list <map> tmp = filter (map e, activated, ``(e[`type]:"" == "mount"));
	foreach (map e, tmp, {
	    target_map = Storage::SetPartitionData (target_map, e[`device]:"",
						    "mount", "");
	});
	Storage::SetTargetMap (target_map);
    }

    /**
     * Unmount all mounted partitions, deactivate swaps, detach loopback
     * devices. Uses list activated to make actions in reverse order.
     * @param keeep_in_target Do not remove mounts from targetmap
     * @return void
     */
    global define void UnmountPartitions (boolean keep_in_target)
    {
	y2milestone ("UnmountPartitions: %1", keep_in_target);

	did_try_mount_partitions = false;

	foreach (map info, activated, {
	    y2milestone ("Unmounting %1", info);
	    string type = info[`type]:"";

	    if (type != "")
	    {
		if (type == "mount")
		{
		    string file = Installation::destdir + info[`mntpt]:"";
		    if (! (boolean)SCR::Execute (.target.umount, file))
		    {
			// error report, %1 is device (eg. /dev/hda1)
			Report::Error (sformat (_("Cannot unmount partition %1.

It is currently in use. If the partition stays mounted,
the data may be lost. Unmount the partition manually
or restart your computer.
"), file));
		    }
		}
		else if (type == "swap")
		{
		    string device = info[`device]:"";
		    // FIXME? is it safe?
		    if (SCR::Execute (.target.bash, "/sbin/swapoff " + device) != 0)
		    {
			y2error("Cannot deactivate swap %1", device);
		    }
		}
		else if (type == "crypt")
		{
		    string dmname = info[`device]:"";
		    dmname = "cr_" + substring( dmname, findlastof( dmname, "/" )+1 );
		    // FIXME? is it safe?
		    if (WFM::Execute(.local.bash, "cryptsetup remove " + dmname) != 0)
		    {
			y2error("Cannot remove dm device %1", dmname);
		    }
		}
	    }
	});

	// now remove the mount points of the mounted partitions
	// in the target map of the storage module
        if (!keep_in_target)
	    RemoveFromTargetMap ();


	// clear activated list
	activated = [];
    }


    /**
     * Add information about mounted partition to internal list.
     * @param partinfo partinfo has to be list with exactly two strings,
     * see description of list "activated"
     * @return void
     */
    define void AddMountedPartition (map <symbol, string> partinfo)
    {
	activated = prepend (activated, partinfo);
	y2debug ("adding %1 yields %2", partinfo, activated);
    }


    /**
     * Check the filesystem of a partition.
     */
    define void FSCKPartition (string partition)
    {
	if (!Mode::test ())
	{
	    symbol detected_fs = Storage::DetectFs (partition);
	    if (detected_fs == `ext2)
	    {
		// label, %1 is partition
		string out = sformat (_("Checking partition %1"), partition);
		UI::OpenDialog (`opt(`decorated ), `Label(out));

		y2milestone ("command: /sbin/e2fsck -y %1", partition);
		SCR::Execute (.target.bash, "/sbin/e2fsck -y " + partition);

		UI::CloseDialog ();
	    }
	}
    }

/**
 * @param string headline (optional; to disable, use "")
 * @param string question
 * @param string button (true)
 * @param string button (false)
 * @param string details (hidden under [Details] button; optional; to disable, use "")
 */
global boolean AnyQuestionAnyButtonsDetails (
    string headline, string question, string button_yes, string button_no, string details
) {
    boolean has_details = true;
    if (details == "" || details == nil) has_details = false;

    boolean has_heading = true;
    if (headline == "" || headline == nil) has_heading = false;

    term heading = (has_heading ?
	`VBox (
	    `Left(`Heading (headline))
	)
	:
	`Empty()
    );

    term popup_def = `Left (`Label (question));

    term details_checkbox = (has_details ?
	`VBox (
	    `VSpacing (1),
	    `Left (`CheckBox (`id (`details), `opt (`notify), _("Show &Details"), false))
	)
	:
	`Empty ()
    );

    term popup_buttons = `VBox (
	`VSpacing (1),
	`HBox (
	    `HSpacing (8),
	    `PushButton (`id (`yes), button_yes),
	    `VSpacing (2),
	    `PushButton (`id (`cancel), button_no),
	    `HSpacing (8)
	),
	`VSpacing (0.5)
    );

    UI::OpenDialog (`opt (`decorated),
	`VSquash (
	    `VBox (
		heading,
		popup_def,
		`Left (`opt(`hstretch), `ReplacePoint (`id(`rp_details), `Empty())),
		details_checkbox,
		popup_buttons
	    )
	)
    );
    UI::SetFocus (`id (`yes));

    any userinput = nil;
    boolean ret = nil;

    while (true) {
	userinput = UI::UserInput();
	
	if (userinput == `yes) {
	    ret = true;
	    break;
	} else if (userinput == `details) {
	    boolean curr_status = (boolean) UI::QueryWidget (`id (`details), `Value);

	    if (curr_status == false) {
		UI::ReplaceWidget (`id (`rp_details), `Empty());
	    } else {
		UI::ReplaceWidget (`id (`rp_details),
		    `MinSize (
			60, 10,
			`RichText (
			    `id (`details_text),
			    `opt (`plainText, `hstretch),
			    details
			)
		    )
		);
	    }
	} else {
	    ret = false;
	    break;
	}
    }

    UI::CloseDialog();

    return ret;
}

/**
 * Function checks the device and returns whether it is OK or not.
 * The read-only FS check is performed for jfs only and only one for
 * one device.
 *
 * @param string mount_type "jfs", "ext2" or "reiser"
 * @param string device, such as /dev/hda3 or /dev/sda8
 * @param string error_message (a reference to string)
 * @return boolean if successfull or if user forces it
 */
define boolean RunFSCKonJFS (string mount_type, string device, string & error_message) {
    // #176292, run fsck before jfs is mounted
    if (mount_type == "jfs" && device != "") {

	if (contains (already_checked_jfs_partitions, device)) {
	    y2milestone ("Device %1 has been already checked...", device);
	    return true;
	}

	UI::OpenDialog (`Label (sformat (_("Checking file system on %1..."), device)));

	y2milestone ("Running fsck on %1", device);
	// -n == Check read only, make no changes to the file system.
	map cmd = (map) SCR::Execute (.target.bash_output, sformat ("fsck.jfs -n %1", device));
	
	UI::CloseDialog();
	
	// failed
	if (cmd["exit"]:nil != 0) {
	    y2error ("Result: %1", cmd);
	    error_message = tostring(cmd["stderr"]:nil);

	    string details = "";
	    if (cmd["stdout"]:"" != "") {
		details = details + cmd["stdout"]:"";
	    }
	    if (cmd["stderr"]:"" != "") {
		details = (details == "" ? "":"\n") +
		    details + cmd["stderr"]:"";
	    }

	    return AnyQuestionAnyButtonsDetails (
		// popup headline
		_("File System Check Failed"),
		sformat (
		    // popup question (continue/cancel dialog)
		    // %1 is a device name such as /dev/hda5
		    _("The file system check of device %1 has failed.

Would you like to continue in the mounting the device?"), device
		),
		Label::ContinueButton(),
		// button
		_("&Skip Mounting"),
		details
	    );
	// succeeded
	} else {
	    // add device into the list of already checked partitions (with exit status 0);
	    already_checked_jfs_partitions = add (already_checked_jfs_partitions, device);
	    y2milestone ("Result: %1", cmd);
	    return true;
	}
    }
    
    return true;
}

/**
 * Mount partition on specified mount point
 * @param mount_point string mount point to monut the partition at
 * @param device string device to mount
 * @param mount_type string filesystem type to be specified while mounting
 * @return string nil on success, error description on fail
 */
define string MountPartition (string mount_point, string device,
    string mount_type)
{
    if (mount_type == "")
	// e.g. -> "reiserfs"
	mount_type = FileSystems::GetMountString (
	    Storage::DetectFs (device), "");

    // #223878, do not call modprobe with empty mount_type
    if (mount_type == "") {
	y2warning ("Unknown filesystem, skipping modprobe...");
    // #211916, sysfs, proc are not modular
    } else if (! contains (non_modular_fs, mount_type)) {
	// #167976, was broken with "-t ", modprobe before adding it
	y2milestone("Calling 'modprobe %1'", mount_type);
	SCR::Execute(.target.modprobe, mount_type, "" );
    } else {
	y2milestone("FS type %1 is not modular, skipping modprobe...", mount_type);
    }

    string error_message = nil;
    if (! RunFSCKonJFS (mount_type, device, error_message)) {
	return error_message;
    }

    if (mount_type != "")
	mount_type = "-t " + mount_type;

    boolean ret = (boolean) SCR::Execute (.target.mount,
	[device, Installation::destdir + mount_point, Installation::mountlog],
	mount_type );
    if (ret)
	return nil;
    else
	return (string)SCR::Read (.target.string, Installation::mountlog);
}



/**
 * Check filesystem on a partition and mount the partition on specified mount
 *  point
 * @param mount_point string mount point to monut the partition at
 * @param device string device to mount
 * @param mount_type string filesystem type to be specified while mounting
 * @return string nil on success, error description on fail
 */
define string FsckAndMount (string mount_point, string device,
    string mount_type)
{
    FSCKPartition (device);

    string ret = MountPartition (mount_point, device, mount_type);

    if (ret == nil)
	AddMountedPartition($[`type : "mount", `device : device, `mntpt : mount_point]);

    y2milestone ("mounting (%1, %2, %3) yield %4", Installation::destdir +
	mount_point, device, mount_type, ret);

    return ret;
}


    /**
     *  Check that the root filesystem in fstab has the correct device.
     */
    define boolean check_root_device (string partition, list <map> fstab,
				      string& found_partition)
    {
	list <map> tmp = filter (map entry, fstab, ``(entry["file"]:"" == "/"));

	if (size (tmp) != 1)
	{
	    y2error ("not exactly one root partition found in fstab");
	    found_partition = "none";
	    return false;
	}

	map root = tmp[0]:$[];

	if (!Storage::DeviceRealDisk (root["spec"]:""))
	{
	    // There's nothing I can check.  Anyway, it's not mounted per device
	    // name so it can't be wrong, in theory.
	    return true;
	}

	return true;
    }

/**
 * Find a mount point in fstab
 * @param fstab a list of fstab entries
 * @param mountpoint string a mount point to find
 * @return string the found partition
 */
define string FindPartitionInFstab (list <map> & fstab, string mountpoint) {
    if (substring (mountpoint, size (mountpoint) - 1, 1) == "/")
	mountpoint = substring (mountpoint, 0, size (mountpoint) - 1);

    list <map> tmp = filter (map entry, fstab, {
	return entry["file"]:"" == mountpoint
	    || entry["file"]:"" == mountpoint + "/";
    });

    if (size (tmp) == 0)
	return nil;

    return tmp[0,"spec"]:"";
}

    // translation from new to old device names
    // such as /dev/sdc4 -> /dev/hdb4
    map <string, string> backward_translation = $[];

    /**
     * Translates FS or Cryptotab (old devices to new ones).
     * Such as /dev/hda5 to /dev/sda5.
     *
     * @param list <map> of definitions to translate
     * @param string key name in map to translate
     * @param string key name in map to keep the old value
     * @return list <map> of translated definitions
     *
     * @see https://bugzilla.novell.com/show_bug.cgi?id=258222
     */
    define list <map> TranslateFsOrCryptoTab (list <map> translate, string key_to_translate, string key_preserve_as) {
	// Check whether there is any hardware information that could be used
	string check_command = sformat ("/usr/bin/find '%1/var/lib/hardware/'", String::Quote (Installation::destdir));
	map cmd = (map) SCR::Execute (.target.bash_output, check_command);

	if (cmd["exit"]:nil != nil) {
	    list <string> files = splitstring (cmd["stdout"]:"", "\n");
	    integer files_count = size (files);
	    if (files_count == nil || files_count <= 2) {
		y2error ("There are only %1 files in /var/lib/hardware/, translation needn't work!", files_count);
	    } else {
		y2milestone ("There are %1 files in /var/lib/hardware/", files_count);
	    }
	}

	// first find a list of values for translation
	list <string> old_names = [];
	foreach (map m, translate, {
	    old_names = add (old_names, m[key_to_translate]:"");
	});

	// translate them
	list <string> new_names = Storage::GetTranslatedDevices (
	    Installation::installedVersion, Installation::updateVersion, old_names
	);

	integer i = 0;

	// replace old values with translated ones
	while (i < size (translate)) {
	    string default_val = translate[i, key_to_translate]:"";
	    string new_val = new_names[i]:default_val;

	    translate[i, key_to_translate] = new_val;
	    translate[i, key_preserve_as]  = default_val;
	    backward_translation[new_val]  = default_val;

	    i = i + 1;
	};

	y2milestone ("Current backward translations: %1", backward_translation);

	return translate;
    }

    /**
     * Register a new fstab agent and read the configuration
     * from Installation::destdir
     */
    void readFsTab (list <map> & fstab) {
	    string fstab_file = Installation::destdir + "/etc/fstab";

	    if (FileUtils::Exists (fstab_file)) {
		SCR::RegisterAgent (.target.etc.fstab, `ag_anyagent(
		  `Description (
		      (`File(fstab_file)),
		      "#\n",                    // Comment
		      false,                    // read-only
		      (`List (
		        `Tuple (
		          `spec (`String("^\t ")),
		          `Separator ("\t "),
		          `file (`String("^\t ")),
		          `Separator ("\t "),
		          `vfstype (`String("^\t ")),
		          `Separator ("\t "),
		          `mntops (`String("^ \t\n")),
		          `Optional(`Whitespace()),
		          `Optional(`freq (`Number())),
		          `Optional(`Whitespace()),
		          `Optional(`passno (`Number())),
		          `Optional(`Whitespace()),
		          `Optional(`the_rest (`String ("^\n")))
		        ),
		        "\n"
		      ))
		    )
		));

		fstab = (list<map>) SCR::Read (.target.etc.fstab);
		
		SCR::UnregisterAgent (.target.etc.fstab);
	    } else {
		y2error ("No such file %1. Not using fstab.", fstab_file);
	    }
    }
    
    /**
     * Register a new cryptotab agent and read the configuration
     * from Installation::destdir
     */
    void readCryptoTab (list <map> & crtab) {
	    string crtab_file = Installation::destdir + "/etc/cryptotab";

	    if (FileUtils::Exists (crtab_file)) {
		SCR::RegisterAgent (.target.etc.cryptotab, `ag_anyagent(
		  `Description (
		      (`File(crtab_file)),
		      "#\n",			// Comment
		      false,			// read-only
		      (`List (
			`Tuple (
			  `loop (`String("^\t ")),
			  `Separator ("\t "),
			  `file (`String("^\t ")),
			  `Separator ("\t "),
			  `mount (`String("^\t ")),
		          `Separator ("\t "),
			  `vfstype (`String("^\t ")),
			  `Separator ("\t "),
			  `opt1 (`String("^\t ")),
			  `Separator ("\t "),
			  `opt2 (`String("^ \t")),
			  `Optional(`Whitespace()),
			  `Optional(`the_rest (`String ("^\n")))
			),
			"\n"
		      ))
		    )
		));
		
		crtab = (list<map>) SCR::Read (.target.etc.cryptotab);
		
		SCR::UnregisterAgent (.target.etc.cryptotab);
	    } else {
		y2milestone ("No such file %1. Not using cryptotab.", crtab_file);
	    }
    }

    boolean FstabHasSeparateVar (list <map> & fstab) {
	string var_device_fstab = FindPartitionInFstab (fstab, "/var");
	y2milestone ("/var partition is %1", var_device_fstab);

	return (var_device_fstab != nil);
    }


    boolean FstabUsesKernelDeviceNameForHarddisks(list<map> fstab)
    {
	// We just want to check the use of kernel device names for hard
	// disks. Not for e.g. BIOS RAIDs or LVM logical volumes.

	// Since we are looking at device names of hard disks that may no
	// longer exist all we have at hand is the name.

	return find(map line, fstab, {

	    string spec = line["spec"]:"error";

	    if (regexpmatch(spec, "^/dev/sd[a-z]+[0-9]+$"))
		return true;
	    if (regexpmatch(spec, "^/dev/hd[a-z]+[0-9]+$"))
		return true;
	    if (regexpmatch(spec, "^/dev/dasd[a-z]+[0-9]+$"))
		return true;

	    return false;

	}) != nil;
    }


    define string MountVarIfRequired (list <map> fstab, string root_device_current, boolean manual_var_mount);


    /**
     * Reads FSTab and CryptoTab and fills fstab and crtab got as parameters.
     * Uses Installation::destdir as the base mount point.
     *
     * @param list <map> ('pointer' to) fstab
     * @param list <map> ('pointer' to) crtab
     * @param string root device
     */
    define boolean read_fstab_and_cryptotab (list <map>& fstab, list <map>& crtab, string root_device_current)
    {
	integer default_scr = WFM::SCRGetDefault ();
	integer new_scr = nil;
	backward_translation = $[];

	if (Stage::initial ())
	{
	    readFsTab (fstab);
	    readCryptoTab (crtab);
	}
	else
	{
	    fstab = (list<map>) SCR::Read (.etc.fstab);
	    crtab = (list<map>) SCR::Read (.etc.cryptotab);
	}

	boolean fstab_has_separate_var = FstabHasSeparateVar (fstab);
	// mount /var
	if (fstab_has_separate_var) {
	    y2warning ("Separate /var partition!");
	    MountVarIfRequired (fstab, root_device_current, false);
	} else {
	    y2milestone ("No separate /var partition found");
	}

	y2milestone ("fstab: %1", fstab);
	fstab = TranslateFsOrCryptoTab (fstab, "spec", "spec_old");
	y2milestone ("fstab: (translated) %1", fstab);

	y2milestone ("crtab: %1", crtab);
	crtab = TranslateFsOrCryptoTab (crtab, "file", "file_old");
	y2milestone ("crtab: (translated) %1", crtab);

	// umount /var
	if (fstab_has_separate_var) {
	    SCR::Execute (.target.umount, Installation::destdir + "/var");
	}

	return true;
    }


    /**
     *
     */
    define boolean PrepareCryptoTab (list <map> crtab, list <map>& fstab)
    {
	integer crypt_nb = 0;

	foreach (map mounts, crtab, {

	    string vfstype = mounts["vfstype"]:"";
	    string mntops  = mounts["opt2"]:"";
	    string loop    = mounts["loop"]:"";
	    string fspath  = mounts["mount"]:"";
	    string device  = mounts["file"]:"";

	    y2milestone ("vfstype:%1 mntops:%2 loop:%3 fspath:%4 device:%5",
			 vfstype, mntops, loop, fspath, device);

	    if (!issubstring (mntops, "noauto"))
	    {
	      boolean again = true;
	      while (again)
	      {
		boolean crypt_ok = true;
		string crypt_passwd = DlgUpdateCryptFs( device, fspath );

		if (crypt_passwd == nil || crypt_passwd == "")
		{
		    crypt_ok = false;
		    again = false;
		}

		y2milestone ("crypt pwd ok:%1", crypt_ok);

		if (crypt_ok)
		{
		    map setloop = $[ "encryption"    : "twofish",
				     "passwd"        : crypt_passwd,
				     "loop_dev"      : loop,
				     "partitionName" : device ];

		    crypt_ok = Storage::PerformLosetup( setloop, false );
		    y2milestone ("crypt ok: %1", crypt_ok);
		    if( crypt_ok )
			loop = setloop["loop_dev"]:"";
		    else
		    {
			// yes-no popup
			again = Popup::YesNo (_("Incorrect password. Try again?"));
		    }
		}

		if (crypt_ok)
		{
		    map add_fs = $[ "file" : fspath,
				    "mntops" : mntops,
				    "spec" : loop,
				    "freq" : 0,
				    "passno" : 0,
				    "vfstype": vfstype ];
		    fstab = prepend (fstab, add_fs);
		    AddMountedPartition ($[`type : "crypt", `device : device]);
		    again = false;
		}
	      }
	    }

	});

	return true;
    }


/**
 * Check if specified mount point is mounted
 * @param mountpoint the mount point to be checked
 * @return boolean true if it is mounted
 */
define boolean IsMounted (string mountpoint) {
    if (substring (mountpoint, size (mountpoint) - 1, 1) == "/")
	mountpoint = substring (mountpoint, 0, size (mountpoint) - 1);

    boolean ret = true;
    foreach (map e, activated, {
	if (e[`type]:"" == "mount"
	    && (e[`mntpt]:"" == mountpoint
		|| e[`mntpt]:"" == mountpoint + "/"))
	{
	    ret = true;
	}
    });
    return ret;
}

    // bugzilla #258563
    boolean CheckBootSize (string bootpart) {
	integer min_suggested_bootsize = 65536;
	if (Arch::ia64()) {
	    min_suggested_bootsize = 204800;
	}

	integer bootsize = nil;

	string cmd = sformat (
	    "/bin/df --portability --no-sync -k '%1/boot' | grep -v '^Filesystem' | sed 's/[ ]\\+/ /g'",
	    Installation::destdir
	);
	map bootsizeout = (map) SCR::Execute (.target.bash_output, cmd);

	if (bootsizeout["exit"]:-1 != 0) {
	    y2error ("Error: '%1' -> %2", cmd, bootsizeout);
	} else {
	    list <string> scriptout = splitstring (bootsizeout["stdout"]:"", " ");
	    y2milestone ("Scriptout: %1", scriptout);
	    bootsize = tointeger (scriptout[1]:"0");
	}

	if (bootsize == nil || bootsize == 0) {
	    y2error ("Cannot find out bootpart size: %1", Installation::destdir);
	    return true;
	}
	
	y2milestone ("Boot size is: %1 recommended min.: %2", bootsize, min_suggested_bootsize);

	// Size of the /boot partition is satisfactory
	if (bootsize >= min_suggested_bootsize) {
	    return true;

	// Less than a hero
	} else {
	    integer current_bs = bootsize / 1024;
	    integer suggested_bs = min_suggested_bootsize / 1024;

	    boolean cont = Popup::ContinueCancelHeadline (
		// TRANSLATORS: a popup headline
		_("Warning"),
		// TRANSLATORS: error message,
		// %1 is replaced with the current /boot partition size
		// %2 with the recommended size
		sformat (
		    _("Your /boot partition is too small (%1 MB).
We recommend a size not less than %2 MB. It might happen, that the
new Kernel does not fit, so it would be safer to either enlarge that partition
or not to use the /boot partition at all.

Would you like to continue updating the current system?"),
		    current_bs,
		    suggested_bs
		)
	    );
	    
	    if (cont) {
		y2warning ("User decided to continue despite small a /boot partition");
		return true;
	    } else {
		y2milestone ("User decided not to continue with small /boot partition");
		return false;
	    }
	}
    }

    /**
     *
     */
    define boolean MountFSTab (list <map> fstab, string& message)
    {
	list allowed_fs = [ "ext", "ext2", "ext3", "ext4", "btrfs", "minix", "reiserfs",
			    "jfs", "xfs", "xiafs", "hpfs", "vfat", "auto", "proc" ];

	// mount sysfs first
	string ret = MountPartition ("/sys", "sysfs", "sysfs");

	if (ret == nil)
	    AddMountedPartition($[`type : "mount", `device : "sysfs", `mntpt : "/sys"]);


	boolean success = true;

	boolean raidMounted = false;

	foreach (map mounts, fstab, {

	    string vfstype = mounts["vfstype"]:"";
	    string mntops  = mounts["mntops"]:"";
	    string spec    = mounts["spec"]:"";
	    string fspath  = mounts["file"]:"";

	    if (contains (allowed_fs, vfstype)
		&& fspath != "/" && (fspath != "/var" || ! IsMounted("/var"))
		&& !issubstring (mntops,"noauto"))
	    {
		y2milestone ("mounting %1 to %2", spec, fspath);

		if ( !Mode::test () )
		{
		    string mount_type = "";
		    if (vfstype == "proc")
		    {
			mount_type = vfstype;
		    }

		    string mount_err = "";
		    while (mount_err != nil)
		    {
			mount_err = FsckAndMount (fspath, spec, mount_type);
			if (mount_err != nil)
			{
			    y2error("mounting %1 (type %2) on %3 failed", spec,
				mount_type, Installation::destdir + fspath);
			    UI::OpenDialog (`VBox (
				`Label (sformat (
				    // label in a popup, %1 is device (eg. /dev/hda1), %2 is output of the 'mount' command
				    _("The partition %1 could not be mounted.

%2

If you are sure that the partition is not necessary for the
update (it is not any system partition), click Continue.
To check or fix the mount options, click Specify Mount Options.
To abort update, click Cancel."),
				    spec, mount_err)
				),
				`VSpacing (1),
				`HBox (
				    `PushButton (`id (`cont), Label::ContinueButton ()),
				    // push button
				    `PushButton (`id (`cmd), _("&Specify Mount Options")),
				    `PushButton (`id (`cancel), Label::CancelButton ())
				)
			    ));
			    symbol act = (symbol)UI::UserInput ();
			    UI::CloseDialog ();
			    if (act == `cancel)
			    {
				mount_err = nil;
				success = false;
			    }
			    else if (act == `cont)
			    {
				mount_err = nil;
			    }
			    else if (act == `cmd)
			    {
				UI::OpenDialog (`VBox (
				    // popup heading
				    `Heading (_("Mount Options")),
				    `VSpacing (0.6),
				    // text entry label
				    `TextEntry (`id (`mp), _("&Mount Point"), fspath),
				    `VSpacing (0.4),
				    // tex entry label
				    `TextEntry (`id (`device), _("&Device"), spec),
				    `VSpacing (0.4),
				    // text entry label
				    `TextEntry (`id (`fs), _("&File System\n(empty for autodetection)"), mount_type),
				    `VSpacing (1),
				    `HBox (

					`PushButton (`id (`ok), Label::OKButton ()),
					`PushButton (`id (`cancel), Label::CancelButton ())
				    )
				));
				act = (symbol)UI::UserInput ();
				if (act == `ok)
				{
				    fspath = (string)UI::QueryWidget (`id (`mp), `Value);
				    spec = (string)UI::QueryWidget (`id (`device), `Value);
				    mount_type = (string)UI::QueryWidget (`id (`fs), `Value);
				}
				UI::CloseDialog ();
			    }
			}
		    }

		    if (fspath == "/boot" || fspath == "/boot/") {
			string checkspec = spec;

			// translates new device name to the old one because
			// storage still returns them in the old way
			if (backward_translation[spec]:nil != nil) {
			    checkspec = backward_translation[spec]:spec;
			}

			if (! CheckBootSize (checkspec)) {
			    success = false;
			}
		    }
		}
	    }  // allowed_fs
	    else if (vfstype == "swap" && fspath == "swap" )
	    {
		y2milestone("mounting %1 to %2", spec, fspath);

		if ( !Mode::test () )
		{
		    string command = "/sbin/swapon ";
		    if ( spec != "" )
		    {
			// swap-partition
			command = command + spec;

			// run /sbin/swapon
			integer ret_from_shell = (integer) SCR::Execute (.target.bash, command);
			if ( ret_from_shell != 0 )
			{
			    y2error("swapon failed: %1", command );
			}
			else
			{
			    AddMountedPartition ($[`type : "swap", `device : spec]);
			}
		    }
		}
	    }
	});

	return success;
    }

/**
 * Mount /var partition
 * @param device string device holding the /var subtree
 * @return string nil on success, error description on fail
 */
string MountVarPartition (string device) {
    string mount_err = FsckAndMount ("/var", device, "");
    string err_message = nil;
    if (mount_err != nil)
    {
	y2error (-1, "failed to mount /var");
	err_message = sformat (
	    // error message
	    _("The /var partition %1 could not be mounted.\n"),
	    device) + "\n" + mount_err + "\n\n" + sdb;
    }
    return err_message;
}

// <-- BNC #448577, Cannot find /var partition automatically
// returns if successful
boolean MountUserDefinedVarPartition () {
    // function return value
    boolean manual_mount_successful = false;

    list <string> list_of_devices = [];
    // $[ "/dev/sda3" : "Label: My_Partition" ]
    map <string, string> device_info = $[];

    // Creating the list of known partitions
    foreach (string device, map description, Storage::GetOndiskTarget(), {
	foreach (map partition, description["partitions"]:[], {
	    // Some partitions logically can't be used for /var
	    if (partition["detected_fs"]:`unknown == `swap) return;
	    if (partition["type"]:`unknown == `extended) return;
	    if (! haskey (partition, "device")) return;

	    list_of_devices = add (list_of_devices, partition["device"]:"");

	    device_info[partition["device"]:""] = sformat (
		// Informational text about selected partition, %x are replaced with values later
		_("<b>File system:</b> %1, <b>Type:</b> %2,<br>
<b>Label:</b> %3, <b>Size:</b> %4,<br>
<b>udev IDs:</b> %5,<br>
<b>udev path:</b> %6"),
		// starts with >`<
		substring (tostring (partition["detected_fs"]:`unknown), 1),
		partition["fstype"]:_("Unknown"),
		partition["label"]:_("None"),
		String::FormatSize (partition["size_k"]:0 * 1024),
		mergestring (partition["udev_id"]:[], ", "),
		partition["udev_path"]:_("Unknown")
	    );
	});
    });

    list_of_devices = sort (list_of_devices);
    y2milestone ("Known devices: %1", list_of_devices);

    while (true) {
	UI::OpenDialog (
	    `VBox (
		`MarginBox (1, 0,
		    `VBox (
			// a popup caption
			`Left (`Heading (_("Unable to find the /var partition automatically"))),
			// a popup message
			`Left (`Label (_("Your system uses a separate /var partition which is required for the upgrade
process to detect the disk-naming changes. Select the /var partition manually
to continue the upgrade process."))),
			`VSpacing (1),
			`Left (`ComboBox (
			    `id ("var_device"),
			    `opt (`notify),
			    // a combo-box label
			    _("&Select /var Partition Device"),
			    list_of_devices
			)),
			`VSpacing (0.5),
			// an informational rich-text widget label
			`Left (`Label (_("Device Info"))),
			`MinHeight (
			    3, `RichText (`id ("device_info"), "")
			),
			`VSpacing (1)
		    )
		),
		`MarginBox (1, 0, `ButtonBox (
		    `PushButton (`id (`ok), `opt (`okButton), Label::OKButton()),
		    `PushButton (`id (`cancel), `opt (`cancelButton), Label::CancelButton())
		))
	    )
	);

	any ret = nil;

	// initial device
	string var_device = (string) UI::QueryWidget (`id ("var_device"), `Value);
	UI::ChangeWidget (`id ("device_info"), `Value, device_info[var_device]:"");

	// to handle switching the combo-box or [OK]/[Cancel]
	while (true) {
	    ret = UI::UserInput();
	    var_device = (string) UI::QueryWidget (`id ("var_device"), `Value);

	    if (ret == "var_device") {
		UI::ChangeWidget (`id ("device_info"), `Value, device_info[var_device]:"");
	    } else {
		break;
	    }
	}

	UI::CloseDialog();

	// Trying user-selection
	if (ret == `ok) {
	    y2milestone ("Trying to mount %1 as /var", var_device);
	    string mount_error = MountVarPartition (var_device);

	    if (mount_error != nil) {
		Report::Error (mount_error);
		continue;
	    } else {
		y2milestone ("Manual mount (/var) successful");
		manual_mount_successful = true;
		break;
	    }
	// `cancel
	} else {
	    y2warning ("User doesn't want to enter the /var partition device");
	    break;
	}
    }

    return manual_mount_successful;
}

/**
 * Check if /var partition is needed, mount it if it is
 * @param fstab a list of fstab entries
 * @param root_device_current string current root device
 * @param manual_var_mount whether to ask user to enter /var device if not found
 * @return string nil on success, error description on fail
 */
define string MountVarIfRequired (list <map> fstab, string root_device_current, boolean manual_var_mount)
{
	string var_device_fstab = FindPartitionInFstab (fstab, "/var");

	// No need to mount "/var", it's not separate == already mounted with "/"
	if (var_device_fstab == nil) {
	    y2milestone ("Not a separate /var...");
	    return nil;
	}

	if (!Storage::DeviceRealDisk (var_device_fstab)) {
	    y2milestone ("Device %1 is not a real disk, mounting...", var_device_fstab);
	    return MountVarPartition (var_device_fstab);
	}

	// BNC #494240: If a device name is not created by Kernel, we can use it for upgrade
	if (!Storage::IsKernelDeviceName (var_device_fstab)) {
	    y2milestone ("Device %1 is not a Kernel device name, mounting...", var_device_fstab);
	    return MountVarPartition (var_device_fstab);
	}

	list <map> tmp1 = filter (map entry, fstab, ``(entry["file"]:"" == "/"));
	string root_device_fstab = tmp1[0,"spec"]:"";
	if (!Storage::DeviceRealDisk (root_device_fstab))
	    return MountVarPartition (var_device_fstab);

	map root_info = Storage::GetDiskPartition (root_device_fstab);
	map var_info = Storage::GetDiskPartition (var_device_fstab);

	if (root_info["disk"]:"" == var_info["disk"]:"")
	{
	    map tmp2 = Storage::GetDiskPartition (root_device_current);
	    string var_partition_current = Storage::GetDeviceName (tmp2["disk"]:"", var_info["nr"]:0);

	    return MountVarPartition (var_partition_current);
	}

	list <string> realdisks = [];
	foreach (string s, map m, (map <string, map>) Storage::GetOndiskTarget (), {
	    // BNC #448577, checking device
	    if (Storage::IsKernelDeviceName(s) && Storage::DeviceRealDisk (s))
		realdisks = add (realdisks, s);
	});

	if (size (realdisks) != 2)
	{
	    // <-- BNC #448577, Cannot find /var partition automatically
	    if (manual_var_mount && MountUserDefinedVarPartition ()) {
		return nil;
	    }

	    y2error ("don't know how to handle more than two disks at this point");
		// error message
	    return _("Unable to mount /var partition with this disk configuration.\n") + sdb;
	}

	string other_disk = realdisks[ realdisks[0]:"" == root_info["disk"]:"" ? 1 : 0 ]:"";
	string var_partition_current = Storage::GetDeviceName (other_disk, var_info["nr"]:0);

	return MountVarPartition (var_partition_current);
}


    /**
     * Mounting root-partition; reading fstab and mounting read partitions
     */
    global define boolean MountPartitions (string root_device_current)
    {
	y2milestone ("mount partitions: %1", root_device_current);

	if (did_try_mount_partitions)
	    return true;

	did_try_mount_partitions = true;

	boolean success = true;

	// popup message, %1 will be replace with the name of the logfile
	string message =  sformat (_("Partitions could not be mounted.\n
Check the log file %1."), Directory::logdir + "/y2log");
	y2milestone ("selected partition: %1", root_device_current);

	boolean ret_bool = true;

	list <map> fstab = [];
	list <map> crtab = [];

	// Mount selected root partition to Installation::destdir
	if ( !Mode::test () )
	{
	    ret_bool = nil == FsckAndMount ("/", root_device_current, "");
	}

	if ( ret_bool )
	{
	    // read the keyboard settings now, so that it used when
	    // typing passwords for encrypted partitions
	    // Calling a script because otherwise this module would depend on yast2-country
	    if (Stage::initial()) {
		WFM::call ("rootpart_check_keyboard", [$["destdir":Installation::destdir]]);
	    }

	    read_fstab_and_cryptotab (fstab, crtab, root_device_current);
	    Update::GetProductName();

	    if (FstabUsesKernelDeviceNameForHarddisks(fstab))
	    {
		y2warning("fstab on %1 uses kernel device name for hard disks", root_device_current);
		Popup::Warning(sformat(_("Some partitions in the system on %1 are mounted by kernel-device name. This is
not reliable for the update since kernel-device names are unfortunately not
persistent. It is strongly recommended to start the old system and change the
mount-by method to any other method for all partitions."), root_device_current));
	    }

	    if (size (fstab) == 0)
	    {
		y2error ("no or empty fstab found!");
		// error message
		message = _("No fstab found.");
		success = false;
	    }
	    else
	    {
		string tmp_msg = MountVarIfRequired (fstab, root_device_current, true);
		if (tmp_msg != nil)
		{
		    y2error ("failed to mount /var!");
		    message = tmp_msg;
		    success = false;
		}
		else
		{
		    string tmp = "";

		    if (!check_root_device (root_device_current, fstab, tmp))
		    {
			y2error ("fstab has wrong root device!");
			// message part 1
			message = _("The root partition in /etc/fstab has an invalid root device.\n") +
			// message part 2
			    sformat (_("It is currently mounted as %1 but listed as %2.\n"),
				     root_device_current, tmp) + sdb;
			success = false;
		    }
		    else
		    {
			y2milestone ("cryptotab %1", crtab);

			PrepareCryptoTab (crtab, fstab);

			y2milestone ("fstab %1", fstab);

			if (!MountFSTab (fstab, message))
			    success = false;
		    }
		}
	    }
	}
	else
	{
	    y2error("Could not mount root '%1' to '%2'", root_device_current,
		    Installation::destdir);
	    success = false;
	}

	y2milestone ("MountPartition (%1) = %2", root_device_current, success);
	y2milestone ("activated %1", activated);

	if (!success)
	{
	    Popup::Message(message);

	    // some mount failed, unmount all mounted fs
	    UnmountPartitions(false);
	    did_try_mount_partitions = true;
	}
	else
	{
	    // enter the mount points of the newly mounted partitions
	    // in the target map of the storage module
	    AddToTargetMap ();
	}

	return success;
    }

    /**
     * Get architecture of an elf file.
     */
    define string GetArchOfELF (string filename)
    {
	map bash_out = (map) SCR::Execute (.target.bash_output, Directory::ybindir +
					   "/elf-arch " + filename);
	if (bash_out["exit"]:1 != 0)
	    return "unknown";
	return deletechars (bash_out["stdout"]:"unknown", "\n");
    }

    /**
     * Checks the partition whether it contains an incomplete installation.
     *
     * @see BNC #441919
     * @param string system mounted to directory
     * @return boolean true if incomplete
     */
    global boolean IncompleteInstallationDetected (string mounted_to) {
	// by default, installation is complete
	boolean ret = false;

	foreach (string check_this, [Installation::run_yast_at_boot], {
	    check_this = sformat ("%1/%2", mounted_to, check_this);
	    if (FileUtils::Exists (check_this) == true) {
		y2milestone ("File %1 exists, installation is incomplete", check_this);
		ret = true;
		break;
	    }
	});

	return ret;
    }

    /**
     * Check a root partition and return map with infomations (see
     * variable rootPartitions).
     */
    define map CheckPartition (map partition)
    {
	map freshman = $[
	    `valid : false,
	    `name : "unknown",
	    `arch : "unknown",
	    `label: partition["label"]:"",
	    `fs : partition["detected_fs"]:`unknown,
	    `fstype : partition["fstype"]:"unknown",
	];

	string p_dev = partition["device"]:"error";
	integer p_fsid = partition["fsid"]:0;
	symbol p_type = partition["type"]:`primary;
	symbol p_detect_fs = partition["detected_fs"]:`unknown;

	// possible root FS
	if (contains(FileSystems::possible_root_fs, p_detect_fs))
	{
	    map<symbol,string> mt_map = $[
		`ext2: "ext2",
		`ext3: "ext3",
		`ext4: "ext4",
		`btrfs : "btrfs",
		`reiser: "reiserfs",
		`xfs: "xfs",
		`jfs: "jfs"
	    ];
	    string mount_type = mt_map[p_detect_fs]:"";

	    string error_message = nil;
	    if (! RunFSCKonJFS (mount_type, p_dev, error_message)) {
		freshman[`valid] = false;
		return freshman;
	    }

	    // mustn't be empty and must be modular
	    if (mount_type != "" && ! contains (non_modular_fs, mount_type))
		SCR::Execute(.target.modprobe, mount_type, "" );
	    // mount (read-only) partition to Installation::destdir
	    Storage::RemoveDmMapsTo( p_dev );
	    if ((boolean) SCR::Execute (.target.mount, [p_dev, Installation::destdir,
							Installation::mountlog], "-o ro"))
	    {
		// Is this a root partition, does /etc/fstab exists?
		if (SCR::Read (.target.size, Installation::destdir + "/etc/fstab") > 0)
		{
		    y2milestone ("found fstab on %1", partition);

		    list <map> fstab = [];
		    list <map> crtab = [];
		    
		    read_fstab_and_cryptotab (fstab, crtab, p_dev);
		    Update::GetProductName();

		    fstab = filter (map p, fstab, ``(p["file"]:""=="/"));

		    if (size(fstab[0,"spec"]:"") == 0) {
			y2warning ("Cannot find / entry in fstab %1", fstab);
		    }

		    freshman[`valid] = size(fstab[0,"spec"]:"")>0 &&
		                 Storage::DeviceMatchFstab( p_dev, 
							    // bugzilla #304269
							    // DeviceMatchFstab expects _old_ not _translated_ device
				                            fstab[0,"spec_old"]:fstab[0,"spec"]:"" );

		    // Why this doesn't match?
		    // Possible reasons:
		    // - /var not mounted so hwinfo cannot translate device names
		    if (freshman[`valid]:false != true) {
			y2warning ("Device does not match fstab: '%1' vs. '%2'", p_dev, fstab[0,"spec"]:"");
		    }
                    if( Mode::autoinst() ) {
                        // we dont care about the other checks in autoinstallation
                        SCR::Execute (.target.umount, Installation::destdir);
                        return freshman;
                    }

		    // Get installed release name
		    string release = SuSERelease::ReleaseInformation
			(Installation::destdir);
                    y2debug("release: %1", release );
		    if (release == "?") {
			// label for an unknown installed system
			release = _("Unknown");
		    }
		    freshman[`name] = release;

		    // Right architecture?
		    freshman[`arch] = GetArchOfELF (Installation::destdir + "/bin/bash");
		    string instsys_arch = GetArchOfELF ("/bin/bash");
		    
		    // `arch_valid, see bugzilla #288201
		    // installed /bin/bash and the one from inst-sys are matching
		    if (freshman[`arch]:"unknown" == instsys_arch) {
			y2milestone ("Architecture (%1) is valid", instsys_arch);
			freshman[`arch_valid] = true;

		    // both are PPC, bugzilla #249791
		    } else if (
			contains(["ppc", "ppc64"], freshman[`arch]:"unknown")
			&&
			contains(["ppc", "ppc64"], instsys_arch)
		    ) {
			y2milestone ("Architecture for partition %1 is %2, upgrading %3", p_dev, freshman[`arch]:"unknown", instsys_arch);
			freshman[`arch_valid] = true;

		    // Architecture is not matching
		    } else {
			y2milestone ("Architecture for partition %1 is %2, upgrading %3", p_dev, freshman[`arch]:"unknown", instsys_arch);
			freshman[`arch_valid] = false;
		    }

		    // If architecture is not matching, the whole partition is considered to be wrong
		    if (freshman[`arch_valid]:false != true) {
			y2milestone ("Architecture is not valid -> the whole partition is not valid");
			freshman[`valid] = false;
		    }

		    if (IncompleteInstallationDetected (Installation::destdir)) {
			y2milestone ("Incomplete installation detected, partition is not valid");
			freshman[`valid] = false;
		    }

		    y2milestone ("Partition is valid: %1, arch is valid: %2", freshman[`valid]:false, freshman[`arch_valid]:false);
		}

		// unmount partition
		SCR::Execute (.target.umount, Installation::destdir);
	    }
	}

	y2milestone ("%1 %2", partition, freshman);

	return freshman;
    }


    /**
     * Find all valid root partitions and place the result in rootPartitions.
     * The partitions are mounted and unmounted again (to Installation::destdir).
     * Loads a bunch of kernel modules.
     * @return void
     */
    global define void FindRootPartitions ()
    {
	if (didSearchForRootPartitions)
	    return;

	ModuleLoading::Load ("reiserfs", "", "Linux", "Reiser FS", Linuxrc::manual (), true);
	ModuleLoading::Load ("jfs", "", "Linux", "JFS", Linuxrc::manual (), true);
	ModuleLoading::Load ("xfs", "", "Linux", "XFS", Linuxrc::manual (), true);
	ModuleLoading::Load ("ext3", "", "Linux", "Ext3", Linuxrc::manual (), true);
	ModuleLoading::Load ("ext4", "", "Linux", "Ext4", Linuxrc::manual (), true);
	ModuleLoading::Load ("btrfs", "", "Linux", "BtrFS", Linuxrc::manual (), true);
	ModuleLoading::Load ("raid0", "", "Linux", "Raid 0", Linuxrc::manual (), true);
	ModuleLoading::Load ("raid1", "", "Linux", "Raid 1", Linuxrc::manual (), true);
	ModuleLoading::Load ("raid5", "", "Linux", "Raid 5", Linuxrc::manual (), true);
	ModuleLoading::Load ("raid6", "", "Linux", "Raid 6", Linuxrc::manual (), true);
	ModuleLoading::Load ("raid10", "", "Linux", "Raid 10", Linuxrc::manual (), true);
	ModuleLoading::Load ("multipath", "", "Linux", "Multipath", Linuxrc::manual (), true);
	ModuleLoading::Load ("dm-mod", "", "Linux", "DM", Linuxrc::manual (), true);
	SCR::Execute (.target.bash, "/sbin/devmap_mknod.sh");
	ModuleLoading::Load ("dm-snapshot", "", "Linux", "DM", Linuxrc::manual (), true);

	if (Mode::test ())
	{
	    Storage::SetTargetMap ((map<string,map>) SCR::Read (.target.yast2, "test_target_map.ycp"));
	}

//	Storage::ActivateEvms();
	map <string, map> target_map = (map <string, map>) Storage::GetOndiskTarget ();
	y2milestone ("target_map: %1", target_map);

	// prepare progress-bar
	if (UI::WidgetExists (`id ("search_progress")))
	    UI::ReplaceWidget (
		`id ("search_progress"),
		`ProgressBar (
		    `id ("search_pb"),
    		    _("Evaluating root partition. One moment please..."),
    		    100,
    		    0
		)
	    );

	rootPartitions = $[];
	numberOfValidRootPartitions = 0;

	// all partitions on all devices
	integer max_steps = 0;
	foreach (string device, map description, target_map, {
	    max_steps = max_steps + size (description["partitions"]:[]);
	});

	integer counter = 0;
	foreach (string device, map description, target_map, {
	    foreach (map partition, description["partitions"]:[], {

		counter = counter + 1;
		if (UI::WidgetExists (`id ("search_progress")))
		    UI::ChangeWidget (`id ("search_pb"), `Value, (100 * counter / max_steps));

		// some partitions don't make sense at all
		if (partition["detected_fs"]:`unknown != `swap &&
		    partition["type"]:`primary != `extended)
		{
		    map freshman = $[];

		    if (Mode::test ())
			freshman = $[`valid : true, `name : "SuSE Linux 4.2", `arch : "i286", `label: "Label" ];
		    else
			freshman = CheckPartition (partition);

		    rootPartitions = add (rootPartitions, partition["device"]:"error", freshman);

		    if (freshman[`valid]:false)
			numberOfValidRootPartitions = numberOfValidRootPartitions + 1;
		}
	    });
	});

	// 100%
	if (UI::WidgetExists (`id ("search_progress")))
	    UI::ChangeWidget (`id ("search_pb"), `Value, 100);

	didSearchForRootPartitions = true;

	y2milestone ("rootPartitions: %1", rootPartitions);
    }

global string GetDistroArch () {
    return GetArchOfELF ("/bin/bash");
}

    global boolean mount_target ()
    {
        UI::OpenDialog (`opt(`decorated ),
                        // intermediate popup while mounting partitions
                        `Label(_("Mounting partitions. One moment please...")));

        boolean tmp = RootPart::MountPartitions (RootPart::selectedRootPartition);
        // sleep (500);

        UI::CloseDialog ();

        return tmp;
    }

global void Detect () {
    if (!didSearchForRootPartitions)
    {
	Wizard::SetContents (
	    // TRANSLATORS: dialog caption
	    _("Update Configuration"),
	    `VBox (
		`ReplacePoint (`id ("search_progress"), `Empty())
	    ),
    	    "",
    	    false,
    	    false
    	);

        FindRootPartitions ();

        selectedRootPartition = "";
	y2milestone ("Detected root partitions: %1", rootPartitions);
    }
}

}

ACC SHELL 2018