/**
 *  File:
 *    OSRFstab.ycp
 *
 *  Module:
 *    YaST OS Repair. Automatic error detection & repair tool for Linux.
 *
 *  Summary:
 *    YaST OS Repair. Automatic error detection & repair tool for Linux.
 *
 *  Author:
 *    Johannes Buchhold <jbuch@suse.de>
 *
 *  Use:
 *      OSRFstab::LinuxPartitions ("/dev/hda1", "/dev/hda2", .. ]);
 *      OSRFstab::MountablePartitions( ..);
 *	OSRFstab::ValidRootPartitions( ..);
 *
 *	OSRFstab::ReadFstab("/");
 *	OSRFstab::CheckRootEntry ("/dev/hda1",
 *		["/dev/hda2"], ["/dev/hda3", "/dev/hda4" ]);
 *	OSRFstab::Repair();
 *
 *	OSRFstab::Check(["/dev/hda2"], ["/dev/hda3", "/dev/hda4" ]);
 *	OSRFstab::Repair();
 *
 *
 * $Id: OSRFstab.ycp 20509 2004-12-07 15:38:00Z jsuchome $
 */
{
  module "OSRFstab";

  import "FileSystems";
  import "Storage";
  import "StorageDevices";
  import "Hotplug";
  import "Partitions";
  import "Mode";
  import "Installation";
  import "AsciiFile";
  import "Initrd";
  import "Report";

  import "OSRFsck";
  import "OSRLogFile";
  import "OSRSystem";
  import "OSRExecute";
  import "OSRPopup";

  import "Label";
  import "Popup";


  textdomain "repair";

  string remove_label		= _("Remove");
  string ignore_label		= _("Ignore");
  string replace_label		= _("Replace");
  string create_label		= _("Create");

  string device_label		= _("Device");
  string mount_label		= _("Mount point");
  string create_mp_label	= _("Create Mount Point");
  string state_label		= _("State");
  string fstab_line_label	= _("Fstab Line");

  // button label
  string b_ignore_label		= _("&Ignore");
  // button label
  string b_create_label		= _("&Create");
  // button label
  string b_remove_label		= _("&Remove");
  // button label
  string b_change_mp_label	= _("Change &Mount Point");
  // button label
  string b_replace_label	= _("R&eplace");

  global list<string> system_mount_points =
    [ "/dev", "/lib", "/bin", "/etc", "/sbin" ];

  /**
   * The path
   */
  global string fstabpath	= "/etc/fstab";

  /**
   * The default fstab entries (as maps) -> set in constructor
   */
  map <string,map> fstab_defaults	= $[];

  /**
   * All digits
   */
  string digits          = "0123456789";

  /**
   * Line position to sense
   */
  map pos2field = $[ 0: "spec",
		     1: "mount",
		     2: "vfstype",
		     3: "mntops",
		     4: "freq",
		     5: "passno" ];


  /**
   * Partitions which are swapable
   */
  list<string> swapable_partition_list   = [];

  /**
   * All checked partitions (OSRFsck)
   */
  list<string> checked_partitions	= [];

  /**
    @example of map describing /etc/fstab:

    $[
	"comment"	: "^[ \t]*#.*",
	"delim"		: " \t",
	"l"		: $[
	    1	: $[
		"fields" : ["/dev/hda2", "/", "reiserfs", "defaults", "1", "1"],
		"line"	 :"/dev/hda2\t/\treiserfs\tdefaults 1 1"
	    ],
	    2	: $[
		"fields" : [
		    "/dev/hda3", "/local", "reiserfs", "defaults", "1", "2"],
		"line"	 : "/dev/hda3\t/local\treiserfs\tdefaults 1 2"
	    ],
	    3	: $[
		"fields" : [ "/dev/hda1", "swap", "swap", "pri=42", "0", "0"],
		"line"	 : "/dev/hda1\tswap\tswap\tpri=42 0 0"
	    ],
	    4	: $[
		"fields" : [
		    "devpts", "/dev/pts", "devpts", "mode=0620,gid=5","0", "0"],
		"line"	 : "devpts\t/dev/pts\tdevpts\tmode=0620,gid=5 0 0"
	    ],
	    ...
	    14	: $[
		"fields" : [
		    "nfs.suse.cz:/home", "/home", "nfs", "defaults", "0", "0"],
		"line"	 : "nfs.suse.cz:/home\t/home\tnfs\tdefaults 0 0"
	    ],
	    15	: $[
		"comment":true,
		"line":"#blah"
	    ]
	],
	"widths"	: [20, 20, 10, 21, 1, 1]
    ]
  */
  map fstab				= $[];

  /**
   * The root mount points e.g.: "/", "/mnt"
   */
  string root_mountpoint		= "";

  /**
   * All checked valid lines
   * e.g.: [1,2,3];
   */
  list<integer> valid_lines		= [];

  /**
   * Not valid lines, e.g.:
   * $[4: ["dev", false, true, true ,true ,true ,true ]],//spec error in line 4
   */
  map not_valid_lines			= $[];

  /**
   * Devices without fstab entry
   * e.g.: ["/dev/hdb7"]
   */
  list<string> missing_devs		= [];

  /**
   * Constructor: initialize data structures with default values
   */
  global define void OSRFstab () {

    foreach (string type, [
	"pts", "proc", "usb", "swap", "cdrom", "floppy",
	"root", "zip", "dev", "nfs", "sys" ],
    {
	fstab_defaults [type]	= FileSystems::GetFstabDefaultMap (type);
    });
  }

  define void ResetSettings() {
      missing_devs	= [];
      valid_lines	= [];
      not_valid_lines   = $[];
  }

  global define void Reset() {
      ResetSettings();
      root_mountpoint		= "";
      fstab			= $[];
      swapable_partition_list   = [];
      checked_partitions	= [];
  }

  /**
   * @param key		values of which key should be returned
   * @param exists	restrict the returned set only to the types with
   * nonempty values of key 'exists'
   */
  define list<string> fstab_default_exist (string exists, string key)
  {
    map <string,map> existing	= filter (
	string type, map desc, fstab_defaults, ``(desc[exists]:"" != ""));
    return (list<string>) maplist (string type, map d, existing, ``(d[key]:""));
  }


  /**
   * e.g.: transform a fstab list to a fstab map.
   * list: ["/dev/hda6", "/", "reiserfs", "defaults", "1", "1"]
   * map : $[ "spec":"/dev/hda6", "file":"/" ... ]
   */
  define map fstabline2map (list<string> line)``{

    map ret	= $[];
    integer pos	= 0;
    foreach (string e, line, ``{
	ret [pos2field[pos]:""] = e;
	pos = pos +1;
    });
    return ret;
  }

  /**
   *  Find the list of all valid linux-partitions in the target-map.
   *  @return list The list of names of valid linux-partitions.
   */
  global define list<string> LinuxPartitions(list checked_partitions) ``{

    if (Mode::test ()) return ["/dev/hda1", "/dev/hda2"];

    if (size (checked_partitions) == 0) return [];

    // e.g.: ["/dev/hda1", "/dev/hdb2"]
    list<string> linux_partition_list		= [];

    foreach (string device , map description, Storage::GetTargetMap(), {

	foreach (map partition, description["partitions"]:[], ``{

	    if ((partition["fsid"]:0 == Partitions::fsid_native ||
		 partition["type"]:`primary == `lvm ||
		 partition["type"]:`primary == `sw_raid))
	    {
		// if the fsck functions have been executed
		if (size (checked_partitions) != 0)
		{
		    if (contains (checked_partitions,
			Storage::GetDeviceName (device, partition["nr"]:nil)))
		    {
			linux_partition_list = add (linux_partition_list,
			  Storage::GetDeviceName (device, partition["nr"]:nil));
		    }
		}
		else
		{
		    linux_partition_list = add (linux_partition_list,
			Storage::GetDeviceName( device, partition["nr"]:nil));
		}
	    }
	});
    });
    if (size ( linux_partition_list) == 0)
	y2error("no linux partition found");
    return linux_partition_list;
  };


  /**
   * Returns a list of names of all mountable partitions out of
   * the specified list.
   * @param list linux_partition_list A list of valid linux-partitions out of
   *	which the mountable partitions are to be returned.
   * @param m_point the test mountpoint
   * @return list List of partition names successfully mounted to m_point.
   */
  global define list<string> MountablePartitions(
			list<string> linux_partition_list, string m_point) ``{

    if (Mode::test () || size (linux_partition_list) == 0)
	return linux_partition_list;

    // e.g.: ["/dev/hda1"]
    list<string> mount_possible_list	= [];

    integer i = 0;
    foreach (string partition_item, linux_partition_list, ``{

	if ((boolean) WFM::Execute (.local.mount,
		[ partition_item, m_point, OSRExecute::OutputFile() ]))
	{
	    mount_possible_list = add (mount_possible_list, partition_item);
	    if (!(boolean) WFM::Execute(.local.umount, m_point))
	    {
		y2error("umounting partition %1 not possible",partition_item);
	    }
	}
	else
	{
	    y2error("mounting partition %1 is not possible", partition_item);
	}
	i = i + 1;
    });
    return mount_possible_list;
  };


  /**
   *  Returns a list of names of all mountable partitions that contain
   *  a file /etc/fstab out of the specified list.
   *  @param list linux_partition_list A list of valid linux-partitions
   *	out of which the mountable partitions are to be returned.
   *  @return list The list of partition-names that were successfully mounted
   *	to /mnt and contain a filesystem table.
   */
  global define list<string> ValidRootPartitions(list<string> mount_possible_list, string mount_p) ``{

    if (Mode::test ()) return mount_possible_list;

    if (size (mount_possible_list) == 0) return [];

    // e.g.: ["/dev/hda1"]
    list<string> valid_root_partitions	= [];
    list<string> fstab_found_partitions	= [];
    list<string> smpoints_found_partitions	= [];

    foreach (string partition_item, mount_possible_list, ``{

	if (! (boolean) WFM::Execute(.local.mount,
		[ partition_item, mount_p , OSRExecute::OutputFile() ]))
	{
	    y2error("Partition: %1; not possible to mount to /mnt",
		partition_item);
	}
	else
	{
	    // Check fstab exist
	    // use the .local.size agent instead of the .etc.fstab agent
	    // because it finds the fstab of currently mounted root partition
	    if (WFM::Read (.local.size, mount_p + fstabpath) > 0)
	    {
		y2milestone("Partition %1: fstab found", partition_item);
		fstab_found_partitions = add (fstab_found_partitions,
		    partition_item);
	    }
	    else
	    {
		y2milestone("Partition: %1; no fstab found", partition_item);
	    }
	    // Check system mount points
	    boolean not_fount = false;
	    foreach (string mp,  system_mount_points, ``{
		if (mp != "/")
		{
		    y2debug ("command: /usr/bin/test -d " +  mount_p + mp);
		    if (! OSRExecute::Command(.local.bash,
			    "/usr/bin/test -d "+ mount_p + mp))
		    {
			  not_fount = true;
		    }
		}
	    });
	    if (! not_fount)
	    {
		y2milestone("system mount points found");
		smpoints_found_partitions = add (smpoints_found_partitions,
		    partition_item);
	    }
	    else
		y2warning("system mount points not found. Partition %1 could not be a root partition", partition_item);
	}
	// umount the partition
	WFM::Execute(.local.umount, mount_p);
    });

    // filter items witch are in both list
    valid_root_partitions = (list<string>) filter (string p, (list<string>)
	union (fstab_found_partitions, smpoints_found_partitions), ``(
	    contains (fstab_found_partitions, p) &&
	    contains (smpoints_found_partitions, p))
    );

    //OSRFstab can create a new fstab.!!
    valid_root_partitions = (list<string>)
	union (valid_root_partitions, smpoints_found_partitions);
    y2milestone("OSRFstab::ValidRootPartitions return %1",
	valid_root_partitions);
    return valid_root_partitions;
  };

  /**
   * Read the fstab file.
   */
  global define boolean ReadFstab(string mount_p , boolean strict)``{

    root_mountpoint	= mount_p;
    fstab		= Partitions::GetFstab(  mount_p + fstabpath);

    if (fstab == nil ||  size (fstab) == 0 || fstab["l"]:$[] == $[])
    {
	y2error("No fstab found !!");
	if (strict) return false;

	// yes/no popup headline
	if (Popup::YesNoHeadline(_("fstab cannot be read."),
	// yes/no popup text
_("
YaST cannot find an existing fstab
file. To create a new one,
press \"Yes\".
")))
	{
	    OSRExecute::Command (.local.bash,
		sformat ("/bin/mv %1%2 %1%2.YaSTsave",root_mountpoint,fstabpath));
	    OSRExecute::Command (.local.bash,
		sformat ("/bin/touch %1%2", root_mountpoint, fstabpath));
	    fstab = Partitions::GetFstab (mount_p + fstabpath);
	    return true;
	}
	return false;
    }
    else
    {
	y2milestone ("fstab :%1 ", fstab);
	return true;
    }
  }

  /**
   * Removes blanks from a string.
   */
  define string remove_blanks (string c) ``{
      return ( mergestring (splitstring (c ," "), ""));
  }

  /**
   * Cat the uuid from a string.
   */
  define string uuid_string(list<string> line) ``{

      if (substring (line[0]:"", 0, 4) == "UUID") {
	  return substring (line[0]:"", 5);
      }
      return "";
  }

  /**
   * Cat the label from a string.
   */
  define string label_string(list<string> line)``{

      if (substring (line[0]:"", 0,5) =="LABEL") {
	  return substring (line[0]:"",6);
      }
      return "";
  }

  /**
   * Check the spec (first) entry in a fstab line.
   */
  define boolean check_fs_spec( list<string> line, map part)``{

    string uuidstring	= uuid_string (line);
    string labelstring	= label_string (line);

    return (contains (checked_partitions, line[0]:"")	||
	    uuidstring   == part["uuid"]:"!"		||
	    labelstring  == part["label"]:"!"
   );
  }

  /**
   * Check the file (second) entry in a fstab line.
   */
  define boolean check_fs_file(list<string> line)``{

    if (line[1]:"" == nil || line[1]:"" == "")
	return false;

    string dir = line[1]:"";
    if (dir == "" || dir == nil)
	return false;

    return (OSRExecute::Command (.local.bash,
	sformat ("/usr/bin/test -d %1%2", root_mountpoint, dir)));
  }

  /**
   * Check the filesystem type (third) entry in a fstab line.
   */
  define boolean check_fs_vfstype(list<string> line, map part) ``{

    if (line[2]:"" == nil || line[2]:"" == "") return false;

    line[2] = remove_blanks (line[2]:"");

    if (!(FileSystems::GetMountString (part["detected_fs"]:`unknown, "!")
	    == line[2]:""
	  || line[2]:"" == "auto" || line[2]:"" == "subfs"))
    {
	return false;
    }
    return true;
  }

  /**
   * Check the mount options (fourth) entry in a fstab line.
   */
  define boolean check_fs_mntops(list<string> line) {

    if (line[3]:"" == nil || line[3]:"" == "") return false;

    map checked = FileSystems::CheckFstabOptions (remove_blanks(line[3]:""));
    if (!checked["all_known"]:true)
    {
	y2error ("unknown mount options: %1",checked["unknown_options"]:"");
    }
    return checked["all_known"]:true;
  }

  /**
   * Check the freq (fifth) entry in a fstab line.
   */
  define boolean check_fs_freq(list<string> line, list<string> defaults)``{
      if (line[4]:"" == nil || line[4]:"" == "") return false;

      return (( size (filterchars (remove_blanks (line[4]:"0"), digits)) > 0) &&
	      ( contains (defaults, remove_blanks (line[4]:"0"))));
  }

  /**
   * Check the passno (sixth) entry in a fstab line.
   */
  define boolean check_fs_passno(list<string> line, list<string> defaults)``{

    if (line[5]:"" == nil || line[5]:"" == "")
	return false;

    if (size (filterchars (remove_blanks (line[5]:"0"), digits))
	== size (remove_blanks (line[5]:"0")))
    {
	if (size (defaults) != 0)
	{
	    return contains (defaults, remove_blanks (line[5]:"!"));
	}
	else
	{
	    return true;
	}
    }
    return false;
  }

  /**
   * Returns the partition map for a device.
   * cdrom, floppy and zip devices are support too.
   */
  define map devname2part(string dev_name)``{

      if (substring (dev_name, 0,1) != "/") return $[];

      map ret = StorageDevices::GetCdromEntry( dev_name);
      if (ret == $[] || ret == nil) {

	  ret = find(map e, StorageDevices::cddrives, ``(e["linkname"]:"" == dev_name));

	  // check link
	  if (ret == nil) {
	      OSRExecute::CommandOutput(.local.bash, sformat ("/bin/ls -l %1", dev_name));
	      list<string> std_dev_list = filter (string s,  splitstring (OSRExecute::stdout, " "), ``( s != " "));
	      string link	= std_dev_list[size (std_dev_list)-1]:"";
	      list linkl	= splitstring (link , "\n");
	      link		= linkl[0]:"";
	      y2milestone("link is %1", link);
	      if (! issubstring (link , "/dev"))
	      {
		  link = "/dev/" + link ;
	      }
	      y2milestone("new link %1", link);
	      ret = StorageDevices::GetCdromEntry( link);
	      if (ret == $[] || ret == nil)
	      {
		  //y2milestone(" StorageDevices :: %1 " ,StorageDevices::cddrives);
		  ret = find(map e, StorageDevices::cddrives, ``(e["linkname"]:"" == link));
	      }
	      y2milestone(" ret in check link %1", ret);
	  }
      }

      if (ret == $[] || ret == nil) {
	  ret =  find(map e, StorageDevices::FloppyDrives, ``(e["dev_orig"]:(e["dev_name"]:"") == dev_name));
	  if (ret != $[] && ret != nil) ret["found"] = "floppy";
      }
      else {
	  ret["found"] = "cdrom";
      }

      if (ret == $[] || ret == nil)
      {
	list lret	= maplist (string n, map m,
	    filter (string k, map e, (map<string,map>)StorageDevices::ZipDrives,
		``(e["dev_orig"]:(e["dev_name"]:"") == dev_name)), ``(m));
	ret		= lret[0]:$[];
	if (ret != $[] && ret != nil) ret["found"] = "zip";
      }

      if (ret == $[] || ret == nil)
      {
	  if (! Storage::IsKeyKey(dev_name)) return $[];
	  // subdevkeys
	  // $["disk":"/dev/hda", "nr":3]
	  map subdevkeys    = Storage::GetDiskPartition( dev_name);
	  string dev_name   = Storage::GetDeviceName(subdevkeys["disk"]:"", subdevkeys["nr"]:nil);
	  string subdevkey  = Storage::GetSubDevKey(subdevkeys["disk"]:"", subdevkeys["nr"]:nil);
	  // root_part
	  // $["detected_fs":`reiser, "device":"/dev/hda3", "fsid":131, "fstype":"Linux native", "mount":"/", "nr":3, "region":[148, 1175],
	  // "type":`primary, "used_fs":`reiser, "uuid":"a7fb4dc2-cd09-41d1-a696-805ea613abee"]
	  ret = Storage::GetSubDev( subdevkey);
	  if (ret != $[] && ret != nil) {
	      if (ret["device"]:"" == dev_name)
	      {
		  ret["dev_name"] = dev_name;
		  ret["subdevkey"] =  subdevkey;
		  if (ret["fsid"]:0 == Partitions::fsid_swap) {
		      ret["found"] = "swap";
		  }
		  else {
		      ret["found"] ="dev";
		  }
		  if (ret["mount"]:"" == "/")
		  {
		      ret["found"] = "root";
		  }
	      }
	      else {
		  // Storage return default !!
		  ret = $[];
	      }
	  }
      }
      return ret;
  };

  /**
   * Check if an entry for a device exist in the fstab.
   */
  define boolean check_dev_entry(string p)``{

    map part  =  devname2part(p);
    map lines =  filter (integer lnr, map line, fstab["l"]:$[], ``(

	(line["fields",0]:"!" == p)				||
	(line["fields",0]:"!" == part["linkname"]:"" &&
	 part["found"]:"" == "cdrom")				||
        (line["fields",0]:"!" == "UUID=" + part["uuid"]:"")	||
        (line["fields",0]:"!" == "LABEL=" + part["label"]:""))
   );
    if (size (lines) > 0)
    {
	y2milestone("found entry for %1 in the fstab", p);
	return true;
    }
    else
    {
	y2warning ("no entry for %1 found in the fstab",p);
	if (part["lvm_group"]:"" != "")
	{
	    y2warning ("it is a device with LVM group: ignoring");
	    return true;
	}
	missing_devs = add (missing_devs, p);
	missing_devs = toset (missing_devs);
	return false;
    }
  }


  /**
   * Return the partition map for a special uuid.
   */
  define map uuid2part(string uuidstring) {
      map ret = $[];
      foreach (string dn, checked_partitions, {
	  if (ret == $[]) {
	      map part = devname2part(dn);
	      if (part ["uuid"]:"" == uuidstring)
	      {
		  ret = devname2part(dn);
		  ret["wise"] = "uuid" ;
	      }
	  }
      });
      return ret;
  }

  /**
   * Return the partition map for a special label.
   */
  define map label2part(string labelstring) {
      map ret = $[];
      foreach (string dn, checked_partitions, {
	  if (ret == $[]) {
	      map part = devname2part(dn);
	      if (part ["label"]:"!" == labelstring) {
		  ret = devname2part( dn);
		  ret["wise"] = "label";
	      }
	  }
      });
      return ret;
  }

  /**
   * Return the partition map for a fstab line.
   */
  define map line2part(list<string> line) ``{

      if (contains (fstab_default_exist ("spec", "spec"), line[0]:""))
	  return $[];

      map ret = devname2part(line[0]:"");
      if (ret == nil || ret == $[])
      {
	  string uuidstring	= uuid_string(line);
	  if (uuidstring != "")
	      ret = uuid2part (uuidstring);
	  if (ret == nil || ret == $[])
	  {
	      string labelstring = label_string(line);
	      if (labelstring != "")
		  ret = label2part( labelstring);
	  }
      }
      else
      {
	  ret["wise"] = "dev";
      }
      y2milestone("line2part return: %1", ret);
      return ret;
  }


  /**
   * Check one fstab line and return false if
   * the line contains errors.
   */
  define boolean check_fstab_line( integer lnr)``{

    map linemap		= AsciiFile::GetLine(fstab, lnr);
    list<string> line	= linemap["fields"]:[];

    // empty line ?
    if (size (filter (string pos, line, ``( pos != "" && pos != " "))) == 0 ||
	line == [])
    {
	y2milestone(" add empty line to valid_lines ");
	valid_lines  = add (valid_lines , lnr);
	return true;
    }

    map part			= line2part(line);

    string  type		= "invalid";
    boolean fs_spec		= false;
    boolean fs_file		= false;
    boolean fs_vfstype		= false;
    boolean fs_mntops		= false;
    boolean fs_freq		= false;
    boolean fs_passno		= false;

    if (part == nil || part == $[] || size (part)<=0)
    {
	if (line[0]:"" == fstab_defaults["usb","spec"]:"!")
	{
	    if (Hotplug::haveUSB)
	    {
		type	= "usb";
		fs_spec	= true;
		fs_file	= (line[1]:"" == fstab_defaults[type,"mount"]:"!");
		fs_vfstype = (line[2]:"" == fstab_defaults[type,"vfstype"]:"!");
		fs_mntops  = check_fs_mntops (line);
	    }
	    else
	    {
		y2error("usb line in fstab but no usb found");
		type		= "usb";
		fs_spec		= false;
		fs_file		= true;
		fs_vfstype	= true;
		fs_mntops	= true;
	    }
	}
	else if (issubstring (line[0]:"", ":/"))
	{
	    // nfs
	    type	= "nfs";
	    fs_spec	= true; // no check
	    fs_file	= check_fs_file (line);
	    fs_vfstype	= true; // no check
	    fs_mntops	= true; // no check
	}
	else if (line[0]:"" == fstab_defaults["pts","spec"]:"!")
	{
	    type	= "pts";
	    fs_spec	= true;
	    fs_file	= check_fs_file (line);
	    fs_vfstype	= ( line[2]:"" == fstab_defaults[type,"vfstype"]:"!");
	    fs_mntops	= ( line[3]:"" != nil && line[3]:"" != "");
	}
	else if (line[0]:"" == fstab_defaults["proc","spec"]:"!")
	{
	    type	= "proc";
	    fs_spec	= true;
	    fs_file	= ( line[1]:"" == fstab_defaults[type,"mount"]:"!");
	    fs_vfstype	= ( line[2]:"" == fstab_defaults[type,"vfstype"]:"!");
	    fs_mntops	= ( line[3]:"" != nil && line[3]:"" != "");
	}
	else if (line[0]:"" == fstab_defaults["sys","spec"]:"!")
	{
	    type	= "sys";
	    fs_spec	= true;
	    fs_file	= ( line[1]:"" == fstab_defaults[type,"mount"]:"!");
	    fs_vfstype	= ( line[2]:"" == fstab_defaults[type,"vfstype"]:"!");
	    fs_mntops	= ( line[3]:"" != nil && line[3]:"" != "");
	}
	fs_freq		= check_fs_freq (
	    line, [ tostring (fstab_defaults [type, "freq"]:0) ]);
	fs_passno	= check_fs_passno (
	    line, [ tostring (fstab_defaults [type, "passno"]:0) ]);
    }
    else
    {
	list<string> special_freq	= [];
	list<string> special_passno	= [];

	// cdrom floppy zip
	if (part["found"]:"" == "floppy" ||
	    part["found"]:"" == "zip"    ||
	    part["found"]:"" == "cdrom")
	{
	    type	= part["found"]:"";
	    fs_spec	= true;
	    fs_file	= check_fs_file(line);
	    fs_vfstype	= check_fs_vfstype(line, part);
	    fs_mntops	= check_fs_mntops( line);
	}
	else if (contains (checked_partitions, part["dev_name"]:"") &&
		 part["found"]:"" == "dev")
	{
	    // check root first with CheckRootEntry!!
	    if (line[1]:"" == "/")
		return true;
	    type	= "dev";
	    fs_spec	= true;
	    fs_file	= check_fs_file(line);
	    fs_vfstype	= check_fs_vfstype(line, part);
	    fs_mntops	= check_fs_mntops( line);

	    special_passno	= [ "2" ];
	    special_freq	= [ "1" ];
	    // add mount point to target_map for later use (OSRBoot)
	    part["mount"]	= line[1]:"";
	    Storage::SetSubDev (part["subdevkey"]:"", part);
	}
	else if (contains (swapable_partition_list, part["dev_name"]:"") &&
		 part["found"]:"" == "swap")
	{
	    type	= "swap";
	    fs_file	= true;
	    fs_spec	= (line[1]:"" == "swap");
	    fs_vfstype	= ( line[2]:"" == "swap");
	    fs_mntops	= ( (substring (line[3]:"",0,4) == "pri=") &&
		(size (filterchars (substring (line[3]:"",4), digits+"-"))
		 == size (substring (line[3]:"",4)))
	    );
	}
	else if (part["found"]:"" == "root")
	{
	    fs_spec	= true;
	    fs_file	= true;
	    fs_vfstype  = true;
	    fs_mntops   = true;
	    fs_freq     = true;
	    fs_passno   = true;
	}
	else
	{
	    y2error("not valid fstab entry found");
	}

	// not check for root
	if (type != "" &&
	    contains (["dev", "swap","floppy", "zip", "cdrom"], type))
	{
	    fs_freq	= check_fs_freq (line,
		add (special_freq, tostring (fstab_defaults [type, "freq"]:0)));
	    fs_passno  = check_fs_passno (line,
		add (special_passno,tostring(fstab_defaults[type,"passno"]:0)));
	}
    }
    if (! fs_spec)
    {
	y2error("%1 spec not valid", line[0]:"");
    }
    if (! fs_file)
    {
	y2error("%1 file not valid", line[0]:"");
    }
    if (! fs_vfstype)
    {
	y2error("%1 fstype not valid",line[0]:"");
    }
    if (! fs_mntops)
    {
	y2error("%1 mntops not valid",line[0]:"");
    }
    if (! fs_freq)
    {
	y2error("%1 freq not valid",line[0]:"");
    }
    if (! fs_passno)
    {
	y2error("%1 passno not valid",line[0]:"");
    }

    if (fs_spec && fs_file && fs_vfstype && fs_mntops && fs_freq && fs_passno)
    {
	valid_lines  = add (valid_lines , lnr);
	y2milestone("fstab line nr %1 entry is valid", lnr);
    }
    else
    {
	y2error("fstab line nr %1 is not valid", lnr);
	not_valid_lines [lnr] =
	    [type, fs_spec, fs_file, fs_vfstype, fs_mntops, fs_freq, fs_passno];
    }
    return (fs_spec && fs_file && fs_vfstype && fs_mntops && fs_freq &&
	    fs_passno);
  }

  /**
   * All device that should be checked.
   */
  define list<string> devices2check()``{
      list<string> dev2check = (list<string>)
	union (
	    union (checked_partitions, swapable_partition_list),
	    [
		fstab_defaults ["usb", "spec"]:"",
		fstab_defaults ["proc", "spec"]:"",
		fstab_defaults ["pts", "spec"]:""
	    ]
	);
      dev2check = (list<string>) union (dev2check,
	maplist (map d, StorageDevices::cddrives,
	    ``(d["dev_orig"]:(d["dev_name"]:""))));

      dev2check = (list<string>) union (dev2check,
	maplist (map d, StorageDevices::FloppyDrives,
	    ``(d["dev_orig"]:(d["dev_name"]:""))));

      dev2check = (list<string>) union (dev2check,
	maplist (string k, map d, (map<string,map>) StorageDevices::ZipDrives,
	    ``(d["dev_orig"]:(d["dev_name"]:""))));

      return dev2check;
  }

  /**
   * Check if all entries in the fstab are valid and
   * if all found devices have an entry in the fstab.
   */
  global define boolean Check(list<string> tswapable_partition_list,
			      list<string> tchecked_partitions) ``{

      ResetSettings();
      swapable_partition_list = tswapable_partition_list;
      checked_partitions      = tchecked_partitions;

      // check all lines in existing fstab file 
      foreach (integer lnr, map line, fstab["l"]:$[], ``{
	  y2milestone("----check fstab line %1 ", lnr);
	  if (! contains (valid_lines, lnr))
	      check_fstab_line(lnr);
      });
      // check all mountable partitions + swap + usb+ proc +...
      foreach (string p, devices2check(), ``{
	  y2milestone("-----check partition and usb,proc,pts %1", p);
	  check_dev_entry(p);
      });

      return ((size (missing_devs) == 0)  && (size (not_valid_lines) == 0));
  }

  /**
   * Build items for table in missing devices dialog.
   */
  define list<term> missing_devs_items( map new_entries)``{
      list<term> item_list = [];
      foreach (string k, map<string,any> e, (map<string,map<string,any> >)new_entries, ``{
	  item_list = add (item_list, `item(`id(k), k, e["mount"]:""));
      });
      return item_list;
  }

  define string find_next_media_file(string type)``{
      integer num = 0;
      string  mp  = "/media/" + type;
      while (true) {
	  if (AsciiFile::FindLineField(fstab, 1,mp) == [])
	      return mp;
	  else {
	      num = num +  1;
	      mp = sformat ("%1%2",mp,num);
	  }
      }
  }

  define list<string> used_mount_points(map fstab)``{

      list<string> ret = [];
      foreach (integer lnr, map ldata, fstab["l"]:$[], ``{
	  ret = add (ret , ldata["fields", 1 ]:"");
      });
      return ret;
  }

  /**
   * Build fstab entries for all existing devices that are not listed
   * in the fstab.
   */
  define map suggest_missing_entries()``{

    if (Mode::test ()) return $[ "/dev/hda1" : $[ "mount" : "/hh" ]];

    include "partitioning/write_fstab.ycp";
    integer other_nr = 1;	// count other mounts -> global for wirte_fstab
    map new_entries = $[];

    missing_devs = sort (string d1, string d2, missing_devs , ``( d1 < d2));

    foreach (string dev, missing_devs, {

	map part      = devname2part(dev);
	if (part != nil && part != $[] &&
	    contains (["dev", "swap", "root"], part["found"]:"" ))
	{
	    part["device"] = dev;
	    integer nr = 1;
	    map fstabentry			= onepartition2fstab (part,nr);
	    list<string> used_mountpoints	= used_mount_points(fstab);
	    string start			= "/data";
	    integer count			= 2;

	    // fstabentry could be empty map for some partitions (e.g.
	    // with LVM group) -> suggest_missing_entries could report error 4
	    while (contains (used_mountpoints , fstabentry["mount"]:""))
	    {
		// find new mount point
		fstabentry["mount"] = sformat ("%1%2", start, count);
		count = count + 1;
		y2milestone("new mount point %1 ", fstabentry["mount"]:"");
	    }
	    if (size (fstabentry)>0)
	    {
		new_entries[dev] =  fstabentry;
	    }
	}
	else if (contains (fstab_default_exist ("spec", "spec"), dev))
	{
	    string type	= "";
	    foreach (string key, map descr, fstab_defaults, {
		if (descr["spec"]:"" == dev)
		    type	= key;
	    });
	    new_entries[dev]	= fstabline2map ((list<string>)
		FileSystems::GetFstabDefaultList (type));
	}
	else if (contains (["zip", "floppy"], part["found"]:""))
	{
	    map fstline		= fstabline2map ((list<string>)
		FileSystems::GetFstabDefaultList (part["found"]:""));
	    fstline["spec"]	= part["dev_orig"]:(part["dev_name"]:"");
	    fstline["mount"]	= find_next_media_file (part["found"]:"");
	    new_entries[dev]	= fstline;
	}
	else if ("cdrom" == part["found"]:"")
	{
	    map ret		= Storage::MakeCdromFstabEntry (part);
	    new_entries[dev]	= ret;
	}
	else {
	    y2error("creating an entry for %1 is not possible (3)", dev);
	}
    });
    if (size (new_entries) == 0) {
	y2error("creating fstab entries is not possible (4)");
    }
    else {
	y2milestone("Suggested new fstab entries %1", new_entries);
    }
    return new_entries;
  }

  /**
   * @param cmp:
   *      fstab line nr  : mount point
   * cmp : $[ 1		 : "/hhh",
   *          2		 : "/mnt/uuu" ];
   *
   * @return create failed for mount points $[ 1        : "/hhh" ];
   */
  global define map check_and_create_mount_points(map cmp) ``{

    map failed = $[];
    foreach (integer lnr, string missing_dir, (map<integer,string>) cmp, ``{
	    if (substring (missing_dir , 0,1) == "/")
	    {
		list<string> subdirs = filter (string  sdir, splitstring (missing_dir, "/"), ``( sdir != ""));
		string mkdir = root_mountpoint + "/";
		foreach (string sdir, subdirs, ``{
		    mkdir = mkdir + "/"+ sdir;
		    if (! OSRExecute::Command(.local.bash, sformat ("/usr/bin/test -d %1", mkdir)))
			OSRExecute::Command(.local.bash, sformat ("/bin/mkdir %1", mkdir));
		});
		if (! OSRExecute::Command(.local.bash, sformat ("/usr/bin/test -d %1", root_mountpoint + missing_dir)))
		{
		    failed[lnr] =  missing_dir;
		}
	    }
	});

	if (size (failed) == 0)
	{
	    // popup message
	    Report::Message(_("
All missing mount points were
created successfully.
"));
	}
	else if (size (failed) > 1)
	{
	    // error popup
	    Report::Error (sformat (_("
Creating the mount points:
%1
was not successful.
"), mergestring ((list<string>)
	    maplist (integer lnr, string dir, (map<integer,string>) failed,``(dir)), "\n")));
	}
	else if (size (failed) == 1)
	{
	    Report::Error(sformat (_("
Creating the mount point:
%1
was not successful.
"), mergestring ((list<string>)
	    maplist (integer lnr, string dir,(map<integer,string>) failed, ``(dir)), "\n")));
	}
	return failed;
  }

  /**
   * Dialog for existing devices that are not listed in the fstab.
   */
  define symbol SuggestMissingEntriesDialog()``{

      map new_entries   = suggest_missing_entries();

      if (size (new_entries) == 0) return `error;

      string help_text = _("
<P><I>YaST</I> did not find a valid fstab
entry for the devices listed in the table.
</P>
<P>
Select the devices for which 
to create a new fstab entry
and enter a mount point.
</P>
<P>After creating mount points for all
devices, press \"Repair\" 
to continue.
</P>
");
      help_text = help_text +

	  OSRPopup::build_label_description (device_label, _("The name of the device for which no valid fstab
entry was found.
")) +

	  OSRPopup::build_label_description (mount_label, _("The mount point for the found device. YaST creates this
mount point in the file system. If you leave the mount point field empty, no fstab entry will be created.
")) +

	  OSRPopup::build_label_description (create_mp_label, _("Add a mount point for a device."));

      OSRPopup::OpenSuggestDialog(_("Add fstab Entries:"),
				  _("
Select the devices for which to
create a new fstab entry and enter a mount point.
"),
				  help_text,
				  `VBox(`Left(`Label(_("Found devices without valid fstab entry:"))),
					`Table(`id(`table), `header(device_label, mount_label), missing_devs_items(new_entries)),
					`Right(`PushButton(`id(`mountpoint), create_mp_label))) ,70 );


    symbol ret       = `ok;
    repeat
    {
	ret = (symbol) UI::UserInput();

	if (ret == `mountpoint)
	{
	    string current = (string)UI::QueryWidget(`id(`table),`CurrentItem);
	    if (current != nil && current != "")
	    {
		string text = OSRPopup::ChangeFieldDialog (
		    new_entries[current,"mount"]:"",
		    // TextEntry label
		    sformat (_("
Enter a mount point
for the device %1:
"), new_entries[current,"spec"]:""));

		if (text != nil)
		{
		    new_entries[current,"mount"] = text;
		    UI::ChangeWidget(`id(`table),
			`Items, missing_devs_items (new_entries));
		}
	    }
	}

	if (ret == `ok)
	{
	    integer changed_size = size (
		filter (string k,map e, (map<string,map<string,any> >)new_entries, ``(
		    e["spec"]:"" != "" && e["mount"]:"" != ""))
	    );
	    if (changed_size == 0)
	    {
		Report::Warning(_("
First create mount points
for the listed devices.
"));
		ret = `notok;
		continue;
	    }
	    else if (changed_size < size (new_entries))
	    {
		// yes/no popup headline
		if (!Popup::YesNoHeadline (_("Some mount point fields are empty."),
		// yes/no popup text
_("
YaST needs valid mount points to
create fstab entries for devices.
You did not enter a mount point
for all devices. If you continue
now, YaST will only create fstab
entries for devices with
mount points. 
Really continue?
")))
		{
		    ret = `notok;
		    continue;
		}
	    }
	    // create mount point (cmp)
	    map cmp	     = $[];
	    integer fack_lnr = 0;
	    foreach (string k, map e, (map<string,map<string,any> >)new_entries, ``{
		if (e["spec"]:"" != "" && e["mount"]:"" != ""){
		    list<string> fstlist = [ k , e["mount"]:"",
					     e["vfstype"]:"", e["mntops"]:"",
					     sformat ("%1",e["freq"]:nil),
					     sformat ("%1",e["passno"]:nil) ];

		    cmp[fack_lnr] =  e["mount"]:"";
		    fack_lnr      =  fack_lnr + 1;
		    AsciiFile::AppendLine( fstab, fstlist);
		}
	    });

	    map failed = check_and_create_mount_points(cmp);
	    if (size (failed) > 0) ret =  `error;
	}
    } until( ret == `ok || ret == `cancel || ret == `error);

    UI::CloseDialog();
    return ret;
  }



  /**
   *
   */
  define map suggest_create_mp()``{

    // found fstab line without valid mount point, e.g. (not_valid_lines):
    // $[4: ["dev", false, true, true ,true ,true ,true ]], //spec error
    list<integer> cmpl =  maplist (integer kk, list vv,
	filter (integer k, list v, (map<integer,list>) not_valid_lines,
	{
	    return
		v[1]:true == true	&&
		v[2]:true == false	&&
		substring (fstab [4, "fields", 1]:"", 0, 1) == "/"  &&
		(fstab_defaults[v[0]:"","mount"]:"" == "" || v[0]:"" == "pts");
	}),
	``(kk)
    );

    if (Mode::test ()) cmpl = [1,2];
    return AsciiFile::GetLines (fstab, cmpl);
  }


  /**
   *
   */
  define list<term> create_mp_items(map cmp)``{
      list<term> ret = [];
      foreach (integer lnr, map v, (map<integer,map<string,any> >)cmp, ``{
	  ret = add (ret,
	    `item (`id(lnr), create_label, v["fields",1]:"", v["fields",0]:""));
      });
      return ret;
  }

  /**
   *
   */
  define symbol SuggestCreateMp()``{
      map missing_create_mp = suggest_create_mp();

      if (size (missing_create_mp) == 0) return `ok;

      // help text
      string help_text = _("
<P><I>YaST</I> checked the /etc/fstab file and could not find
valid mount points for the listed devices.
</P>") +
	  OSRPopup::build_label_description (state_label, sformat (_("
The status of a listed line can be switched between
%1 and %2. YaST only creates a mount point for a partition
if the the status is %2.
"), ignore_label, create_label)) +


	  OSRPopup::build_label_description (mount_label, _("
This field contains the present entry
of the mount point in fstab.
Change this field to a valid mount point.
 
If you do not want to create the mount point,
select the corresponding line in the table and change
the status to ignore.
")) +
	  OSRPopup::build_label_description (b_ignore_label, sformat (_("\nChange the status of the line to %1.
"),ignore_label)) +

	  OSRPopup::build_label_description (b_create_label, sformat (_("\nChange the status of the line to %1.
"), create_label));

      OSRPopup::OpenSuggestDialog( _("Create mount points:"),
				   _("
Some mount points for fstab entries are missing in
the file system. Change the status of the
lines for which YaST should create a mount point.
"),
				   help_text,
				   `VBox(`Left(`Label(_("Found missing or invalid mount points:"))),
					 `Table(`id(`table), `header( state_label, mount_label, device_label ),
						create_mp_items(missing_create_mp)),
					  `HBox(`PushButton(`id(`ignore), b_ignore_label),
						`PushButton(`id(`create), b_create_label),
						`Right(`PushButton(`id(`change),  b_change_mp_label))),
					 `VSpacing(1)
					 ), 50);

    UI::SetFocus(`id(`table));
    symbol ret       = `ok;
    map    cmp       = $[];
    repeat
    {
	ret = (symbol) UI::UserInput();
	integer current_item  = (integer)
	    UI::QueryWidget(`id(`table), `CurrentItem);

	if (ret == `ignore)
	{
	    UI::ChangeWidget (`id(`table), `Item(current_item,0), ignore_label);
	}
	else if (ret == `create)
	{
	    UI::ChangeWidget (`id(`table), `Item(current_item,0), create_label);
	}
	else if (ret == `change)
	{
	    integer current = (integer)
		UI::QueryWidget(`id(`table), `CurrentItem);
	    if (current != nil)
	    {
		string text = OSRPopup::ChangeFieldDialog (
		    // TextEntry label
		    missing_create_mp [current,"fields",1]:"", sformat (_("
Enter a mount point
for the device %1:
"), missing_create_mp[current,"fields",0]:""));


		if (text != nil)
		{
		    missing_create_mp[current,"fields",1] = text;
		    UI::ChangeWidget (`id(`table), `Items,
			create_mp_items (missing_create_mp));
		}
	    }
	}
	else if (ret == `ok)
	{
	    cmp = $[];
	    foreach (integer e, map lines, (map<integer,map>) missing_create_mp,
	    {
		term it	= (term) UI::QueryWidget(`id(`table), `Item(e));
		if (it[1]:"" == create_label)
		{
		    cmp[e] = lines["fields", 1]:"";
		}
	    });
	}
    } until( ret == `ok || ret == `cancel);

    UI::CloseDialog();

    if (ret == `ok)
    {
	map failed = check_and_create_mount_points( cmp);

	if (size (failed) == 0)
	{
	    foreach (integer lnr, string dir, (map<integer,string>) cmp,``{
		fstab["l",lnr,"fields",1] = dir;
		// rebuild the hole entry
		not_valid_lines[lnr,3] = false;
		not_valid_lines[lnr,4] = false;
		not_valid_lines[lnr,5] = false;
		not_valid_lines[lnr,6] = false;
	    });
	}
	else if (size (failed) > 1)
	{
	    // suggest to remove the line
	    foreach (integer lnr, string dir, (map<integer,string>) failed, ``{
		not_valid_lines = filter (integer llnr, list<any> v, (map<integer,list<any> >)not_valid_lines,
		    ``( llnr != lnr));
	    });
	    ret = `error;
	}
	else if (size (failed) == 1)
	{
	    // suggest to remove the line
	    foreach (integer lnr, string dir, (map<integer,string>) failed, ``{
		not_valid_lines = filter (integer llnr, list<any> v, (map<integer,list<any> >)not_valid_lines,
		    ``( llnr != lnr));
	    });
	    ret = `error;
	}
    }
    return ret;
  }

  /**
   * Find all fstab entries (line nr) with not existing devices.
   */
  define list<integer> suggest_remove_spec()``{
    // spec not valid (false) -> remove
    list<integer> rm = (list<integer>) maplist (integer n, list m,
	filter (integer k, list<any>v, (map<integer,list<any> >)not_valid_lines, ``( v[1]:true == false)),
	``(n));
    return rm;
  }

  /**
   * Build a item list for all fstab entries with not existing devices.
   */
  define list<term> remove_spec_items(list<integer> rm_entries, string ignore_label)
  {

    list<term> ret = [];
    foreach (integer lnr, rm_entries, {
	map line	= AsciiFile::GetLine (fstab, lnr);
	ret = add (ret, `item (`id(lnr), ignore_label, line["line"]:""));
    });
    return ret;
  }

  /**
   * Dialog for all fstab entries with not existing devices.
   */
  define symbol SuggestRemoveSpecDialog()``{
    list<integer> rm_entries = suggest_remove_spec();

    if (size (rm_entries) == 0) return `ok;

    string help_text = OSRPopup::build_label_description (state_label,
	// help text in fstab dialog
	sformat (_("
The status of a listed line can be switched between
%1 and %2. YaST only removes fstab entries if the the status of the
corresponding line is %2.
"), ignore_label, remove_label)) +

	OSRPopup::build_label_description (b_ignore_label, sformat (_("\nChange the status of the line to %1
"), ignore_label)) +

	OSRPopup::build_label_description (b_remove_label, sformat (_("\nChange the status of the line to %1
"), remove_label));

    OSRPopup::OpenSuggestDialog(
	// dialog caption
	_("Remove fstab Entries:"),
	// dialog text
	_("Could not find existing devices for the following fstab lines.
To remove the lines from fstab, select them in the table and 
change the status.
"),
	help_text,
	`VBox(
	    // label (table will follow)
	    `Left(`Label(_("Invalid or Obsolete Lines in fstab:"))),
	    `Table(`id(`table),
		`header(state_label, fstab_line_label),
		remove_spec_items(rm_entries, ignore_label)),
	    `Left(`HBox(
		`PushButton(`id(`ignore), b_ignore_label),
		`PushButton(`id(`remove), b_remove_label)))
	), 50);

    UI::SetFocus(`id(`table));

    symbol ret = `ok;
    repeat
    {
	ret = (symbol) UI::UserInput();
	integer current_item =
	    (integer) UI::QueryWidget(`id(`table), `CurrentItem);

	if (ret == `ignore)
	{
	    UI::ChangeWidget(`id(`table), `Item(current_item, 0), ignore_label);
	}
	else if (ret == `remove)
	{
	    UI::ChangeWidget(`id(`table), `Item(current_item, 0), remove_label);
	}
	else if (ret == `ok)
	{
	    list<integer> rm_list = [];
	    foreach (integer e, rm_entries, {
		term it	= (term) UI::QueryWidget(`id(`table), `Item(e));
		if (it[1]:"" == remove_label)
		{
		    rm_list = add (rm_list, e);
		}
	    });
	    y2milestone("lines to remove %1", rm_list);
	    AsciiFile::RemoveLines(fstab, rm_list);
	}
    } until( ret == `ok || ret == `cancel);

    UI::CloseDialog();
    return ret;
  };

  /**
   * Find all fstab lines with at least one invalid position
   */
  define map suggest_modify()``{

    y2milestone("starting modify suggestion");
/*
    map modify_entries = filter (integer k, list<any> v, (map<integer,list<any> >)not_valid_lines,
	``( v[1]:false == true));
    */
    map modify_entries = filter (integer k, list v,
	(map<integer,list>) not_valid_lines,``( v[1]:false == true));

    if (size (modify_entries) == 0) return $[];

    map nlines  = $[];
    foreach (integer lnr, list opts,  (map<integer,list>) modify_entries,
    {
	map linemap		= AsciiFile::GetLine (fstab, lnr);
	list<string> line	= linemap ["fields"]:[];
	list<string> nline	= [line[0]:""];
	map part		= line2part(line);
	string type		= opts[0]:"";
	list default_line	= FileSystems::GetFstabDefaultList (type);
	if (opts[2]:true == false)
	{
	    if (fstab_defaults [type,"mount"]:"" != "" && type != "pts")
	    {
		nline = add (nline, fstab_defaults[type,"mount"]:"");
	    }
	    else
	    {
		AsciiFile::RemoveLines(fstab, [ lnr ]);
		return;
	    }
	}
	else
	{
	    nline = add (nline , line[1]:"");
	}
	// invalid  vfstype
	if (opts[3]:true == false)
	{
	    if (type == "dev" )
	    {
		string detected_fs = FileSystems::GetMountString (
		    part["detected_fs"]:part["used_fs"]:`unknown, "");
		if (detected_fs != "")
		{
		      nline = add (nline, detected_fs);
		}
		else
		{
		    y2error("could not repair the defect fstab line %1", line);
		    return;
		}
	    }
	    else
	    {
		// proc, usb, pts
		string fs = fstab_defaults[type,"vfstype"]:"";
		if (fs != "")
		    nline = add (nline, fs);
		else
		{
		    y2error("invalid type");
		    return;
		}
	    }
	}
	else
	{
	    nline = add (nline, line[2]:"");
	}
	integer i = 4;
	while (i <= 6)
	{
	    // invalid ops
	    if (opts[i]:true == false)
	    {
		string str = default_line[i-1]:"";
		if (str != "")
		    nline = add (nline, str);
		else
		{
		    y2error("not valid %2 for line %1 found", line,
			pos2field[i-1]:"");
		}
		//set following pos to false
		integer ii = i+1;
		while (ii <=6)
		{
		    opts[ii] = false;
		    ii = ii+1;
		}
	    }
	    else
	    {
		nline = add (nline, line[i-1]:"");
	    }
	    i = i +1;
	}
	nline = filter (string pos, nline, ``( pos != nil && pos != ""));
	if (size (nline) == 6 && nline != line)
	{
	    nlines[lnr] = nline;
	}
	else if (nline != line)
	{
	   y2error("the size of the suggested fstab entry is to less");
	}
    });
    return nlines;
  }

  /**
   * Build a item list.
   */
  define list<term> modify_items (map nlines, string ignore_label, boolean existing)
  {

    list<term> ret = [];
    foreach (integer lnr, list<string> nline,(map<integer,list<string> >)nlines,
    {
	if (existing)
	{
	    map linemap	= AsciiFile::GetLine(fstab,lnr);
	    nline	= linemap["fields"]:[];
	}
	ret = add (ret,`item(`id(lnr), ignore_label, mergestring(nline, ", ")));
    });
    return ret;
  };

  /**
   * Dialog for all fstab entries that have at least one invalid entry.
   */
  define symbol SuggestModifyDialog()``{

    map nlines			= suggest_modify();

    if (size (nlines) == 0)
	return `ok;

    //label of table
    string existing_label	= _("Existing fstab Lines:");
    //label of table
    string suggested_label	= _("Suggested fstab Lines:");

    // help text
    string help_text = sformat (_("
One or more existing fstab entries seem to
be invalid. Compare the suggested entry
with the existing one.
To replace an existing line with a suggested
line, select the corresponding line
in the \"%1\" table and
press \"%2\".
"), suggested_label, b_replace_label) +

    OSRPopup::build_label_description (state_label, sformat (_("
The status of a listed line can be switched between
%1 and %2. YaST only replaces lines if the status is %2.
"), ignore_label, replace_label)) +

    OSRPopup::build_label_description (b_ignore_label, sformat (_("\nChange the status of the line to %1
"), ignore_label)) +

    OSRPopup::build_label_description (b_replace_label, sformat (_("\nChange the status of the line to %1
"), replace_label));

    // dialog headline
    OSRPopup::OpenSuggestDialog (_("Change Existing fstab Entries:"),
    // dialog description
_("
One or more existing fstab entries seem invalid. 
Compare the suggested entry with the existing one."),
	help_text,
	`VBox (
	    `Left(`Label(existing_label)),
	    `Table (`id(`table_e), `opt(`disabled),
		`header (state_label, fstab_line_label),
		modify_items (nlines, ignore_label, true)
	    ),
	    `VSpacing(1),
	    `Left(`Label(suggested_label)),
	    `Table(`id(`table_s), `opt(`notify),
		`header (state_label, fstab_line_label),
		modify_items(nlines,ignore_label, false)
	    ),
	    `Left (`HBox(
		`PushButton(`id(`ignore), b_ignore_label),
		`PushButton(`id(`replace),b_replace_label)
	    ))
	),
	80
    );
    UI::SetFocus(`id(`table_s));

    symbol ret	= `ok;
    repeat
    {
	ret = (symbol) UI::UserInput();
	integer current_item =
	    (integer) UI::QueryWidget(`id(`table_s), `CurrentItem);
	UI::ChangeWidget (`id(`table_e), `CurrentItem, current_item);

	if (ret == `ignore)
	{
	    UI::ChangeWidget(`id(`table_s), `Item(current_item,0),ignore_label);
	    UI::ChangeWidget(`id(`table_e), `Item(current_item,0),ignore_label);
	}
	else if (ret == `replace)
	{
	    UI::ChangeWidget(`id(`table_s),`Item(current_item,0),replace_label);
	    UI::ChangeWidget(`id(`table_e),`Item(current_item,0),replace_label);
	}
	else if (ret == `ok)
	{
	    integer changed_count = size (
		filter (integer e, any ld, (map<integer,any>) nlines, {
		    term it	= (term)UI::QueryWidget(`id(`table_s),`Item(e));
		    return (it[1]:"" == replace_label);
		})
	    );
	    if (changed_count == 0)
	    {
		Report::Warning(_("
Set the status of at least one
table entry to replace.
"));
		ret = `notok;
		continue;
	    }
	    else if (changed_count < size (nlines))
	    {
		// yes/no popup headline
		if (! Popup::YesNoHeadline (_("Some table entries do have not have the status \"replace\"."),
		// yes/no popup text
_("
YaST only replaces the fstab lines marked with
\"replace\". To accept the entire YaST
suggestion, press \"No\" to return to
the dialog and change all lines to the status
replace.
If YaST should only change the selected fstab
lines, press \"Yes\".
")))
		{
		    ret = `notok;
		    continue;
		}
	    }
	    list<integer> rp_list = [];
	    foreach (integer e, any ld, (map<integer,any>) nlines, {
		term it	= (term) UI::QueryWidget (`id(`table_s),`Item (e));
		if (it[1]:"" == replace_label)
		{
		    rp_list = add (rp_list, e);
		}
	    });

	    y2milestone("lines to remove %1", rp_list);
	    AsciiFile::RemoveLines(fstab, rp_list);

	    // save and reread before append fstab !!
	    // Remove don't resort list positions!!
	    AsciiFile::RewriteFile (fstab, root_mountpoint + fstabpath);
	    ReadFstab (root_mountpoint, true);

	    foreach (integer rp, rp_list, ``{
		AsciiFile::AppendLine(fstab, nlines[rp]:[]);
	    });
	}
    } until( ret == `ok || ret == `cancel);

    UI::CloseDialog();
    return ret;
  }

  define symbol ret_worst_symbol(list<symbol> retl)``{

    map symbol_weight = $[
			    `ok		: 1,
			    `cancel	: 2,
			    `error	: 3,
			    `abort	: 2
    ];

    symbol ret = `ok;
    foreach (symbol r, retl, ``{
	if (symbol_weight[ret]:0 < symbol_weight[r]:-1)
	    ret = r;
    });
    return ret;
  }

  /**
   * Repair the fstab. Call sub dialogs.
   */
  global define symbol Repair () ``{

    list<symbol> ret = [];

    if (Mode::test ())
    {
	return `ok;
    }

    if (size (missing_devs) > 0) {
	y2milestone ("-------------- missing devices: %1", missing_devs);
	ret = add (ret, SuggestMissingEntriesDialog());
    }

    if (size (not_valid_lines) > 0) {
	y2milestone ("-------------- not valid lines: %1", not_valid_lines);
	ret = add (ret, SuggestCreateMp());
	ret = add (ret, SuggestRemoveSpecDialog());
	ret = add (ret, SuggestModifyDialog());
    }

    y2milestone("ret repair list %1", ret);

    if ((size (filter (symbol r, ret, ``( r == `ok))) > 0))
    {
	AsciiFile::RewriteFile (fstab, root_mountpoint + fstabpath);
	ResetSettings ();
	ReadFstab (root_mountpoint, true);
    }
    else
    {
	y2milestone("new fstab was not written");
	return `cancel;
    }
    return ret_worst_symbol(ret);
  }

  /**
   *
   */
  global define boolean CheckRootEntry (string dev_name,
					list<string> tswapable_partition_list,
					list<string> tchecked_partitions)``{

    ResetSettings();
    y2milestone("check root fstab entry");
    swapable_partition_list	= tswapable_partition_list;
    checked_partitions		= tchecked_partitions;
    boolean ret			= false;
    map root_part		= devname2part (dev_name);
    list root_l_no		= AsciiFile::FindLineField (fstab, 0, dev_name);

    root_l_no			= union (root_l_no,
	AsciiFile::FindLineField (fstab, 0, "UUID=" + root_part["uuid"]:""));

    root_l_no			= union (root_l_no,
	AsciiFile::FindLineField (fstab, 0, "LABEL=" + root_part["label"]:""));

    // root_lines example:
    // $[ 1 : $["fields":["/dev/hda3", "/", "reiserfs", "defaults", "1", "2"],
    //		"line":"/dev/hda3\t/\treiserfs\tdefaults 1 2" ]
    // ]
    map<integer,map> root_lines		= $[];

    foreach (integer n, (list<integer>) root_l_no, {
	root_lines[n] = AsciiFile::GetLine(fstab, n);
    });

    root_lines	=
	filter (integer n, map l, root_lines, ``(l["fields",1]:"" == "/"));

    if (size (root_lines) != 1)
    {
	y2error(" several or non root entries in fstab found");
	y2error(" root lines %1", root_lines);
	missing_devs  = add (missing_devs, dev_name);
	ret =  false;
    }
    else
    {
	list keys		= maplist (integer k, map v, root_lines,``(k));
	integer root_line_key	= keys[0]:1;
	list<string> root_line	= root_lines [root_line_key, "fields"]:[];
	string type		= "root";

	boolean fs_vfstype	= check_fs_vfstype (root_line, root_part);
	boolean fs_mntops	= check_fs_mntops (root_line);
	boolean fs_freq		= check_fs_freq (root_line,
		[ tostring (fstab_defaults [type, "freq"]:0) ]);
	boolean fs_passno	= check_fs_passno (root_line,
		[ tostring (fstab_defaults [type, "passno"]:0) ]);

	if (! fs_vfstype)
	{
	    y2error("root fstype not valid");
	}
	if (! fs_mntops)
	{
	    y2error("root mntops not valid");
	}
	if (! fs_freq)
	{
	    y2error("root freq not valid");
	}
	if (! fs_passno)
	{
	    y2error("root passno not valid");
	}

	if (fs_vfstype && fs_mntops && fs_freq && fs_passno)
	{
	    valid_lines  = add (valid_lines, root_line_key);
	    y2milestone("root fstab line entry is valid");
	}
	else
	{
	    y2error("root fstab line is not valid");
	    not_valid_lines [root_line_key] =
		[ type, true, true, fs_vfstype, fs_mntops, fs_freq, fs_passno];
	}
	ret = (fs_vfstype && fs_mntops && fs_freq && fs_passno);
    }

    //for OSRBoot
    root_part["mount"] = "/";
    Storage::SetSubDev (root_part["subdevkey"]:"", root_part);

    //inst_prepdisk for OSRBoot!
    foreach (string modname, (list<string>)FileSystems::GetNeededModules (
		    root_part["detected_fs"]:(root_part["used_fs"]:`ext2)), ``{
	  Initrd::AddModule (modname, "");
    });

    return ret;
  }


  /**
   *
   */
  global define string BootDev() ``{

    map lines	= fstab["l"]:$[];
    list line   = maplist (integer llineno, map llinedata,
	filter (integer lineno, map<string,any> linedata, (map<integer,map<string,any> >) lines , ``(
	    linedata["fields", 1]:"" == "/boot")),
	``(llinedata)
    );
    string ret	= "";
    if (size (line) == 1)
    {
	map part	= line2part(line[0,"fields"]:[]);
	ret		= part["dev_name"]:part["device"]:"";
    }
    else
    {
	y2milestone("no /boot device in fstab");
    }
    if (ret == nil)
	ret = "";
    return ret;
  }


  /**
   *
   */
  global define string SelectRoot (list<string> valid_root_partition_list) ``{

    if (size (valid_root_partition_list) == 1)
	return valid_root_partition_list[0]:"";

    return OSRPopup::RadioButtonGroup (
	// popup headline
	_("Select a root partition."),
	// popup text (radio buttons wil follow
	_("There are several valid root partitions in your system.
Select one item in the list.
"),
	valid_root_partition_list,
	"",
	true
    );
  }

  /**
   *
   */
  global define boolean ReadedSuccessfully() ``{
      return ( fstab != nil  && fstab != $[]);
  }

  /**
   *
   */
  global define list<map> UmountAll (list<map> umount_list) ``{

    integer counter_error       = 0;
    list<map> just_umounted     = [];
    list<map> mnt_list		= [];

    mnt_list = filter (map s, umount_list, ``(
	substring (s["file"]:s["mountpoint"]:"", 0, size (root_mountpoint))
	== root_mountpoint)
    );

    mnt_list = sort (map x, map y, mnt_list, ``(
	size (splitstring (x["file"]:x["mountpoint"]:"", "/")) >
	size (splitstring (y["file"]:y["mountpoint"]:"" ,"/")))
    );

    y2milestone("partitions which should be umounted %1", mnt_list);

    // umount all partitions from "/mnt" or deeper, as long as there are no
    // more mounted partitions with mountpoint "/mnt..."
    foreach (map current, mnt_list, ``{

	if (OSRExecute::Command (.local.bash,
		"/bin/umount "+ current["file"]:current["mountpoint"]:""))
	{
	    just_umounted = add (just_umounted, $[
		"partition"  : current["spec"]:current["partition"]:"",
		"mountpoint" : current["file"]:current["mountpoint"]:"" ]
	    );
	}
	else
	{
	    y2error(" %1 could not be umounted",
		current["file"]:current["mountpoint"]:"");
	    counter_error = counter_error +1;
	}
    });

    if (counter_error == 0)
    {
	y2debug("All partitions were successfully umounted from /mnt...");
    }
    return just_umounted;
  }

  /**
   *
   */
  global define list<map> UmountAllFrom( string root)``{

    if (root != "" && root != nil)
	root_mountpoint = root;

    list<map> just_umounted      = [];
    list<map> mounted_partitions = Partitions::CurMounted();

    // filter swap partitions
    mounted_partitions = filter (map p, mounted_partitions, ``(
	p["file"]:"" != "swap" && findfirstof (p["spec"]:"", "/") == 0));

    // filter the list of partitions that are mounted to "/mnt" or deeper
    if (root_mountpoint != "/")
    {
	just_umounted = UmountAll( mounted_partitions);
    }

    return just_umounted;
  }


  /**
   *
   */
  global define list<map> RootDev(string root)``{

    if (root != "" && root != nil)
	root_mountpoint = root;

    list<map> mounted = [];

    if (! ReadedSuccessfully())
    {
	list<string> checked_partitions		= filter (string s,
	    maplist (map p, OSRFsck::PossiblePartitions(), ``(p["device"]:"")),
		``(s !=""));
	list<string> linux_partition_list	=
	    LinuxPartitions(checked_partitions);
	list<string> mount_possible_list	=
	    MountablePartitions (linux_partition_list, root_mountpoint);
	list<string> valid_root_partitions	=
	    ValidRootPartitions (mount_possible_list, root_mountpoint);

	if (size (valid_root_partitions) == 0)
	{
	    // error popup: no root partition found
	    Report::Error(_("
YaST cannot find a valid
root partition. Go back
and select all scan options.
Afterwards, restart scanning.
"));
	    return nil;
	}
	string root_partition	= SelectRoot(valid_root_partitions);
	y2milestone("selected root_partition %1" ,root_partition);

	boolean mount		= OSRExecute::Command (.local.bash,
	    sformat ("/bin/mount %1 %2", root_partition, root_mountpoint));

	mounted = add (mounted, $[
	    "partition" : root_partition,
	    "mountpoint" : root_mountpoint,
	    "status"    : mount ]
	);
	if (mount)
	{
	    OSRSystem::ChangeRoot("/");
	    ReadFstab(root_mountpoint, true);
	}
	else
	{
	    y2error("can not mount root partition");
	}
    }

    y2milestone("RootDev in Fstab %1" ,mounted);
    return mounted;
  }


  /**
   * Mount all partitions specified in the fstab.
   * If no fstab was readed try to find root partition
   * and read fstab. Afterwards mount all partitions.
   */
  global define list<map> MountAll(string root)``{

    list<map> mounted = [];

    if (root != "" && root != nil)
	root_mountpoint = root;

    if (OSRFsck::LoadAllFsModules())
    {
	y2milestone("kernel fs modules successful loaded");
    }
    else
    {
	y2error("loaded kernel modules returned an error");
    }
    // if fstab is not readed.
    // Fstab functions are not called.
    // user skiped execution of osr_module_partition,
    // but need a root partition -> find root and
    // read fstab.
    list<map> t_mounted = RootDev(root_mountpoint);

    if (t_mounted != nil && t_mounted != [])
	mounted = (list<map>) union (mounted, t_mounted);

    if (! ReadedSuccessfully())
    {
	Report::Error(_("
YaST cannot read the file /etc/fstab.
Initialization of the target system
is not possible. Go back
and select all scan options.
"));
	return nil;
    }

    //sort fstab lines /mnt/hh/kk > /mnt/hh
    list<map> lines = (list<map>) maplist (integer lnr, map linedata,
	fstab["l"]:$[], ``(linedata));

    foreach (map linedata, lines, ``{

	list<string> line	= linedata["fields"]:[];
	map part		= line2part (line);
	// not mounted
	if ((part["found"]:"" == "dev") &&
	    (!contains (["/dev/cdrecorder", "/dev/cdrom", "/dev/dvd",
			 "/media/cdrecorder", "/media/cdrom", "/media/dvd" ],
		line[1]:"")) &&
	    (Partitions::MountedOn (line[1]:"", Partitions::CurMounted()) == "")
	    && ( line[1]:"" != "/"))
	{
	    string mp	= root_mountpoint + line[1]:"";
	    mounted	= add (mounted , $[
		"partition"	: line[0]:"",
		"mountpoint"	: mp,
		"status"	: OSRExecute::Command (.local.bash,
		    sformat ("/bin/mount %1 %2", line[0]:"", mp))
		]
	    );
	}
      });

      OSRSystem::ChangeRoot(root_mountpoint);
      y2milestone(" Mount all return %1", mounted); 
      return mounted;
  }


}//EOF
