/**
 * Module:	RootPart.ycp
 *
 * Authors:	Arvin Schnell <arvin@suse.de>
 *
 * Purpose:	Responsible for searching of root partitions and
 *		mounting of target partitions.
 *
 * $Id: RootPart.ycp 22127 2005-03-02 10:01:34Z jsrain $
 */
{
    module "RootPart";

    textdomain "update";

    import "Directory";
    import "Mode";
    import "Linuxrc";
    import "Storage";
    import "Popup";
    import "ModuleLoading";
    import "FileSystems";
    import "Update";
    import "Packages";

    include "partitioning/partition_defines.ycp";
    include "partitioning/custom_part_helptexts.ycp";
    include "partitioning/custom_part_dialogs.ycp";


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

    // 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 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 "loop".
     *
     *   `device   The device.
     *
     *   `mntpt    The mount point, only for `type = "mount".  Does not
     *             include Installation::destdir.
     */
    list <map <symbol, string> > activated = [];


    /*
     *  Information about old and new product.
     *
     *  They do contain:
     *    name (string), e.g. "SuSE Linux"
     *    version (string), e.g. "9.1"
     *    nameandversion (string), e.g. "SuSE Linux 9.1"
     *
     *  They may contain:
     *    major (integer), e.g. "9"
     *    minor (integer, e.g. "1"
     *
     *  These are (hopefully) the same as in the Update module but we can't
     *  ask the package database what product is installed before mounting
     *  /var.  Yes, life is hard.
     */

    // Version of the targetsystem
    global map <string, any> installedVersion = $[];

    // Version of the source medium
    global map <string, any> updateVersion = $[];


    /**
     *  Link to SDB article concerning renaming of devices.
     */
    string sdb = sformat (_("See the SBD article at %1 for details
about how to solve this problem."), "http://portal.suse.com/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]:$[];
        // Unkown for unknown system
	return i[what]:_("Unknown");
    }


    /**
     * Make a nice name for a system out of the long name.
     */
    define string MakeNiceName (string longname)
    {
	integer p1 = find (longname, "(");
	if (p1 == -1) return longname;
	while (p1 > 1 && substring (longname, p1 - 1, 1) == " ")
	    p1 = p1 - 1;
	return substring (longname, 0, p1);
    }


    /**
     * Read release information.
     */
    define string GetRelease ()
    {
	string release = "?";

	release = (string) SCR::Read (.target.string, [Installation::destdir + "/etc/SuSE-release", "?"]);
	if (release != "?")
	{
	    list lines = splitstring (release, "\n");
	    return MakeNiceName (lines[0]:"?");
	}

	release = (string) SCR::Read (.target.string, [Installation::destdir + "/etc/UnitedLinux-release", "?"]);
	if (release != "?")
	{
	    list lines = splitstring (release, "\n");
	    return MakeNiceName (lines[0]:"?");
	}

	release = (string) SCR::Read (.target.string, [Installation::destdir + "/etc/redhat-release", "?"]);
	if (release != "?")
	{
	    list lines = splitstring (release, "\n");
	    return lines[0]:"?";
	}

	return "?";
    }


    /**
     * 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 ();
	list <map> tmp = filter (map e, activated, ``(e[`type]:"" == "mount"));
	foreach (map e, tmp, {
	    target_map = Storage::SetPartitionData (target_map, e[`device]:"",
						    "mount", e[`mntpt]:"");
	});
	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 == "loop")
		{
		    string device = info[`device]:"";
		    // FIXME? is it safe?
		    if (WFM::Execute(.local.bash, "losetup -d " + device) != 0)
		    {
			y2error("Cannot deactivate loopback device %1", device);
		    }
		}
	    }
	});

	SCR::Write(.lvm.deactivate, "");
	SCR::Write(.md.deactivate, "");


	// 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 = FileSystems::DetectFs (partition);
	    if (detected_fs == `ext2)
	    {
		// label, %1 is partition
		string out = sformat (_("Checking partition %1"), partition);
		UI::OpenDialog (`opt(`decorated ), `Label(out));

		sleep (1000);

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

		UI::CloseDialog ();
	    }
	}
    }


    /**
     *
     */
    define boolean fsck_and_mount (string mount_point, string device, string mount_type)
    {
	if (mount_type == "")
	    mount_type = FileSystems::GetMountString (FileSystems::DetectFs (device), "");

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

	FSCKPartition (device);

	boolean ret = (boolean) SCR::Execute (.target.mount, [device,
							      Installation::destdir + mount_point,
							      Installation::mountlog],
					      mount_type );

	if (ret)
	    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;
	}

	if (root["spec"]:"" != partition)
	{
	    y2error ("root partition in fstab has wrong device");
	    found_partition = root["spec"]:"";
	    return false;
	}

	return true;
    }


    define boolean fstab_has_var (list <map> fstab, string& found_partition)
    {
	list <map> tmp = filter (map entry, fstab, ``(entry["file"]:"" == "/var"));

	if (size (tmp) == 0)
	{
	    found_partition = "none";
	    return false;
	}

	found_partition = tmp[0,"spec"]:"";
	return true;
    }


    /**
     *
     */
    global define boolean GetProductName ()
    {
	installedVersion = $[];
	updateVersion = $[];

	/*
	 *  Be careful: The maps returned by from Pkg::TargetProducts and
	 *  Pkg::SourceProductData have different content.
	 */

	// get old product name

	string old_name = GetRelease ();

	integer p = findlastof (old_name, " ");
	if (p == nil)
	{
	    y2error ("release info <%1> is screwed", old_name);
	    installedVersion = $[];
	}
	else
	{
	    installedVersion["name"] = substring (old_name, 0, p);
	    installedVersion["version"] = substring (old_name, p + 1);
	    Update::fill_version_map (installedVersion);
	}

	// get new product name

	integer num = size (Packages::theSources);
	if (num <= 0)
	{
	    y2error ("No source");
	    updateVersion["name"] = "?";
	    updateVersion["version"] = "?";
	    Update::fill_version_map (updateVersion);
	    return false;
	}

	map new_product = Pkg::SourceProductData (Packages::theSources[num-1]:0);
	y2milestone ("First source product %1", new_product);
	if (new_product == nil)
	{
	    y2error ("Screwed source");
	    updateVersion["name"] = "?";
	    updateVersion["version"] = "?";
	    Update::fill_version_map (updateVersion);
	    return false;
	}

	updateVersion["name"] = new_product["productname"]:"?";
	updateVersion["version"] = new_product["productversion"]:"?";
	Update::fill_version_map (updateVersion);

	y2milestone ("update from %1 to %2", installedVersion, updateVersion);

	return true;
    }


    /**
     *
     */
    define list <map> TranslateFSTab (list <map> fstab)
    {
	list <string> old_names = [];
	foreach (map m, fstab, {
	    old_names = add (old_names, m["spec"]:"");
	});

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

	integer i = 0;
	while (i < size (fstab))
	{
	    string spec = fstab[i,"spec"]:"";
	    fstab[i,"spec"] = new_names[i]:spec;
	    i = i + 1;
	};

	return fstab;
    }


    /**
     *
     */
    define boolean read_fstab_and_cryptotab (list <map>& fstab, list <map>& crtab)
    {
	if (SCR::Execute (.target.bash, "/bin/cp " + Installation::destdir + "/etc/fstab /etc") == 0)
	{
	    fstab = (list<map>) SCR::Read (.etc.fstab);
	}

	if ((SCR::Read (.target.size, Installation::destdir + "/etc/cryptotab") > 0)
	    && (SCR::Execute (.target.bash, "/bin/cp " + Installation::destdir + "/etc/cryptotab /etc") == 0))
	{
	    crtab = (list<map>) SCR::Read (.etc.cryptotab);
	}

	if (SCR::Read (.target.size, Installation::destdir + "/etc/raidtab") > 0)
	{
	    SCR::Execute (.target.bash, "/bin/cp " + Installation::destdir + "/etc/raidtab /etc");
	}

	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);

	    boolean crypt_ok = true;

	    if (!issubstring (mntops, "noauto"))
	    {
		if (crypt_nb == 0)
		{
		    SCR::Execute(.target.modprobe, "loop_fish2", "");
		    crypt_nb = crypt_nb + 1;
		}

		string crypt_passwd = DlgUpdateCryptFs( device, fspath );

		if (crypt_passwd == nil)
		{
		    crypt_ok = 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)
		{
		    map add_fs = $[ "file" : fspath,
				    "mntops" : mntops,
				    "spec" : loop,
				    "freq" : 0,
				    "passno" : 0,
				    "vfstype": vfstype ];
		    fstab = prepend (fstab, add_fs);
		    AddMountedPartition ($[`type : "loop", `device : device]);
		}
	    }

	});

	return true;
    }


    /**
     *
     */
    define boolean var_is_mounted ()
    {
	boolean ret = false;

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

	return ret;
    }


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

	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" || !var_is_mounted())
		&& !issubstring (mntops,"noauto"))
	    {
		y2milestone ("mounting %1 to %2", spec, fspath);

		if ( !Mode::test () )
		{
		    // Checking for raid systems
		    if ( issubstring( spec,"/dev/md")
			 && !raidMounted )
		    {
			y2milestone( "Raidarray for partition %1 on device %2 found",
				     fspath, spec );
			string command = "/sbin/raidstart --all --configfile " +
			    Installation::destdir + "/etc/raidtab";

			integer ret_from_shell = (integer) SCR::Execute (.target.bash, command);
			if ( ret_from_shell != 0 )
			{
			    y2milestone ("raidstart failed ( does no matter ): %1", command );
			}
			else
			{
			    y2milestone ("raidstart OK: %1", command );
			    raidMounted = true;
			}
		    }

		    string mount_type = "";
		    if (vfstype == "proc")
		    {
			mount_type = vfstype;
		    }

		    boolean ret_bool = false;
		    while ( !ret_bool )
		    {
			ret_bool = fsck_and_mount (fspath, spec, mount_type);
			if (! ret_bool)
			{
			    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 file name
				    _("The partition %1 could not be mounted.
Check the log file %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, Directory::logdir + "/y2log")
				),
				`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)
			    {
				ret_bool = true;
				success = false;
			    }
			    else if (act == `cont)
			    {
				ret_bool = true;
			    }
			    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 ();
			    }
/*
			    message = sformat (_("The partition %1 could not be mounted.\n
Check the log file %2.
"), spec, Directory::logdir + "/y2log");
			    success = false;
			    ret_bool = true;*/
			}
		    }
		}
	    }  // 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;
    }


    /**
     *
     */
    define boolean mount_var_if_required (list <map> fstab, string root_device_current,
					  string& message)
    {
	string var_device_fstab = "";
	if (!fstab_has_var (fstab, var_device_fstab))
	    return true;

	if (!Storage::DeviceRealDisk (var_device_fstab))
	{
	    if (!fsck_and_mount ("/var", var_device_fstab, ""))
	    {
		y2error ("failed to mount /var");
		// error message
		message = sformat (_("The /var partition %1 could not be mounted.\n"),
				   var_device_fstab) + sdb;
		return false;
	    }

	    return true;
	}

	list <map> tmp1 = filter (map entry, fstab, ``(entry["file"]:"" == "/"));
	string root_device_fstab = tmp1[0,"spec"]:"";

	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);

	    if (!fsck_and_mount ("/var", var_partition_current, ""))
	    {
		y2error ("failed to mount /var");
		// error message
		message = sformat (_("The /var partition %1 could not be mounted.\n"),
				     var_partition_current) + sdb;
		return false;
	    }

	    return true;
	}

	list <string> realdisks = [];
	foreach (string s, map m, (map <string, map>) Storage::GetOndiskTarget (), {
	    if (Storage::DeviceRealDisk (s))
		realdisks = add (realdisks, s);
	});

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

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

	if (!fsck_and_mount ("/var", var_partition_current, ""))
	{
	    y2error ("failed to mount /var");
	    // error message
	    message = sformat (_("The /var partition %1 could not be mounted.\n"),
				 var_partition_current) + sdb;
	    return false;
	}

	return true;
    }


    /**
     * 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 = [];

	SCR::Write(.md.activate, "");
	SCR::Write(.lvm.activate, "");

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

	if ( ret_bool )
	{
	    GetProductName ();

	    read_fstab_and_cryptotab (fstab, crtab);

	    y2milestone ("fstab: %1", fstab);
	    y2milestone ("crtab: %1", crtab);

	    fstab = TranslateFSTab (fstab);
	    crtab = TranslateFSTab (crtab);

	    y2milestone ("fstab: %1", fstab);
	    y2milestone ("crtab: %1", crtab);

	    if (size (fstab) == 0)
	    {
		y2error ("no or empty fstab found!");
		// error message
		message = _("No fstab found.");
		success = false;
	    }
	    else
	    {
		if (!mount_var_if_required (fstab, root_device_current, message))
		{
		    y2error ("failed to mount /var!");
		    success = false;
		}
		else
		{
		    fstab = TranslateFSTab (fstab);
		    crtab = TranslateFSTab (crtab);

		    y2milestone ("fstab: %1", fstab);
		    y2milestone ("crtab: %1", crtab);

		    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;
    }


    global define boolean SetFormatPartitions (list<map> fstabpart)
    {
        map <string, map> target_map = Storage::GetTargetMap ();

	list <map> tmp = filter (map e, activated, ``(e[`type]:"" == "mount"));

	foreach (map e, tmp, {

	    string mntpt = e[`mntpt]:"";
	    string part = e[`device]:"";

	    map p = select(filter(map pp, fstabpart, ``(pp["mount"]:"" == mntpt)), 0 , $[]);
	    boolean format_partition = false;
	    if (haskey(p, "format"))
	    {
		format_partition = p["format"]:false;
	    }
	    target_map = Storage::SetPartitionData (target_map, part, "mount", mntpt );
	    target_map = Storage::SetPartitionData (target_map, part, "format", format_partition);
	    target_map = Storage::SetPartitionData (target_map, part, "delete", false);
	    target_map = Storage::SetPartitionData (target_map, part, "create", false);
	});

        Storage::SetTargetMap (target_map);
        return true;
    }


    /**
     * 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");
    }


    /**
     * 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" ];

	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;

	list possible_root_fs = [ `ext2, `ext3, `reiser, `xfs, `jfs ];

	if ((p_fsid == 131 || p_type == `lvm || p_type == `sw_raid) &&
	    contains (possible_root_fs, p_detect_fs))
	{
	    // mount (read-only) partition to Installation::destdir
	    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);

		    // Get installed release name
		    string release = GetRelease ();
                    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");
		    if (freshman[`arch]:"unknown" == GetArchOfELF ("/bin/bash"))
		    {
			freshman[`valid] = true;
		    }
		    else
		    {
			y2milestone ("Architecture for partition %1 is %2, upgrading %3", p_dev, freshman[`arch]:"unknown", GetArchOfELF ("/bin/bash"));
		    }
		}

		// 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 ("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 ("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) SCR::Read (.target.yast2, "test_target_map.ycp"));
	}

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

	rootPartitions = $[];
	numberOfValidRootPartitions = 0;

	SCR::Write(.md.activate, "");
	SCR::Write(.lvm.activate, "");

	foreach (string device, map description, target_map, {
	    foreach (map partition, description["partitions"]:[], {

		// 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" ];
		    else
			freshman = CheckPartition (partition);

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

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

	SCR::Write(.lvm.deactivate, "");
	SCR::Write(.md.deactivate, "");

	didSearchForRootPartitions = true;

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

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

}
