/**
 * File:
 *   write_fstab.ycp
 *
 * Module:
 *   Partitioning
 *
 * Summary:
 *   Write fstab and cryptotab defines
 *
 * Authors:
 *   Michael Hager <mike@suse.de>
 *   Johannes Buchhold <jbuch@suse.de>
 *
 * $Id: write_fstab.ycp 19964 2004-10-26 15:37:35Z fehr $
 *
 *
 */
{
    import "Arch";
    import "Hotplug";
    import "StorageDevices";
    import "Storage";
    import "FileSystems";
    import "Partitions";
    import "Installation";
    import "AsciiFile";
    
    textdomain "storage";

    include "partitioning/partition_defines.ycp";

//-----------------------------------------------------
// convert partitions to fstab entries
// return map (might be empty)
define map onepartition2fstab (map part, integer& other_nr)
    ``{
    y2milestone( "onepartition2fstab part=%1", part );
    if (part["delete"]:false || 
	part["type"]:`unknown == `extended ||
	(part["type"]:`unknown == `lvm && size(part["mount"]:"")==0) ||
	(part["type"]:`unknown == `evms && size(part["mount"]:"")==0) ||
	(part["loop_fs"]:false && !part["noauto"]:false && 
	 part["crypt"]:"none"!="none") ||
	part["lvm_group"]:"" != "" ||
	part["raid_name"]:"" != "" ||
	part["evms_vol"]:"" != "" ||
	part["fsid"]:0 == Partitions::fsid_prep_chrp_boot ||
	(part["fsid"]:0 == Partitions::fsid_lvm && is( part["nr"]:(any)"", integer)) )
	{
	return $[];
	}

    string spec = part["device"]:""; 
    if( part["mountby"]:`device == `label && size(part["label"]:"")>0 )
	{
	spec = sformat("LABEL=%1", part["label"]:"" );
	}
    else if( part["mountby"]:`device == `uuid && size(part["uuid"]:"")>0 )
	{
	spec = sformat("UUID=%1", part["uuid"]:"" );
	}
    y2debug( "spec=%1", spec );
    string  mount_point = part["mount"]:"";
    integer fsid = part["fsid"]:0;

    symbol  used_fs = part["used_fs"]:`ext2;
    boolean format  = part["format"]:false;

    string  vfstype = "unknown";	// keep "unknown", used again below
    integer freq    = 0;
    integer passno  = 0;
    string  mntops  = part["fstopt"]:"";

    if( mount_point == "swap" )
	{
	vfstype = "swap";
	if( size(mntops) == 0 )
	    {
	    mntops = FileSystems::GetFstabDefaultMntops( "swap" );
	    }
	passno = 0;
	}
    else if( fsid==Partitions::fsid_native || fsid==Partitions::fsid_lvm ||
             (part["type"]:`unknown == `evms && 
	      part["detected_fs"]:`none!=`unknown) )
	{
	vfstype = FileSystems::GetMountString( used_fs, 
	                                       (format ? "ext2" : "auto"));

	freq = 1;
	if( mount_point == "/" )
	    {
	    passno = 1;
	    }
	else if( mount_point != "" )
	    {
	    passno = 2;
	    }
	else if( Stage::initial () && !Arch::s390 () )
	    {
	    mount_point = "/data" + other_nr;
	    // Don't mount and fsck this filesystem during boot, its
	    // state is unknown.
	    mntops = "noauto,user";
	    vfstype = "auto";
	    freq = 0;
	    passno = 0;
	    other_nr = other_nr + 1;
	    y2milestone( "TT add MountPoint %1", mount_point );
	    }
	}
    else if( (Arch::i386 ()||Arch::ia64 ()||Arch::x86_64 ()) && size(mount_point)>0 &&
             (used_fs==`fat32 || used_fs==`ntfs) &&
             (contains(union(union(Partitions::fsid_dostypes, 
				   Partitions::fsid_ntfstypes),
			     Partitions::fsid_wintypes), fsid ) ||
	      fsid==Partitions::fsid_gpt_boot))
	{
	freq = 0;
	passno = 0;
	string lower_point = tolower( mount_point );
	if( lower_point != "" && mount_point != lower_point)
	    {
	    if( Installation::scr_destdir != "/" )
		{
		lower_point = Installation::scr_destdir + lower_point;
		}
	    y2milestone( "symlink %1 -> %2", 
	                 substring(mount_point,(findlastof(mount_point,"/")+1)),
			 lower_point );
	    SCR::Execute(.target.symlink, 
	                 substring(mount_point,(findlastof(mount_point,"/")+1)),
	                 lower_point);
	    }
	vfstype = FileSystems::GetMountString( used_fs, "auto" );
	}
    else if( (Arch::sparc () || Arch::alpha ()) &&
	     contains (Partitions::fsid_skipped, fsid))
	{
	return $[];		// skip "whole disk" partition
	}
    else
	{
	return $[];	        // unknown type
	}
    if( part["detected_fs"]:`reiserfs == `unknown || 
        part["noauto"]:false )
	{
	passno = 0;
	}

    map ret = $[ "spec":spec, 
                 "mount":mount_point, 
		 "vfstype":vfstype,
                 "mntops":mntops, 
		 "freq":freq, 
		 "device":part["device"]:"", 
		 "passno":passno ];

    if( part["loop_fs"]:false )
	{
	ret["freq"] = 0;
	ret["passno"] = 0;
	ret["loop_dev"] = part["loop_dev"]:"";
	mntops = FileSystems::RemoveCryptOpts( mntops );
	if(size(mntops)>0)
	    {
	    mntops = mntops + ",";
	    }
	mntops = mntops + "loop=" + part["loop_dev"]:"";
	if( part["crypt"]:"none" != "none" )
	    {
	    mntops = mntops + ",encryption=" + part["crypt"]:"";
	    }
	if( size(part["phash"]:"")>0 )
	    {
	    mntops = mntops + ",phash=" + part["phash"]:"";
	    }
	if( size(part["itercountk"]:"")>0 )
	    {
	    mntops = mntops + ",itercountk=" + part["itercountk"]:"";
	    }
	ret["mntops"] = mntops;
	if( part["type"]:`none == `loop )
	    {
	    ret["spec"] = part["fpath"]:"";
	    ret["loopfile"] = true;
	    }
	}

    if( size(ret["mntops"]:"")==0 )
	{
	ret["mntops"] = "defaults";
	}

    if( part["fstabline"]:0 != 0 )
	{
	ret["line"] = part["fstabline"]:0;
	}
    y2milestone( "ret=%1", ret );
    return( ret );
    };


//-----------------------------------------------------

// needs "map fstab" globally

define void allpartitions2fstab( map & fstab, list<map> partitions, integer& other_nr ) 
    ``{
    foreach (map partition, partitions, 
	``{
	map fstabentry = onepartition2fstab(partition, other_nr);

	if (size(fstabentry)>0 && size(fstabentry["mount"]:"")>0)
	    {
	    string mount_point = fstabentry["mount"]:"";
	    // mount_point not enough !! ( two swap mount points )!!
	    if( mount_point=="swap" )
		{
		mount_point = mount_point + fstabentry["spec"]:"";
		}
	    // add to map, key is mount point
	    fstab[mount_point] = fstabentry;
	    }
	});
    };


define map onepartition2cryptotab( map part, integer loop_num )
    ``{
    string  partitionName = part["device"]:"";
    string  mountPoint    = part["mount"]:"";
    symbol  used_fs       = part["used_fs"]:`unknown;
    boolean crypt_fs      = part["loop_fs"]:false;
    string  loop_dev      = part["loop_dev"]:"";

    if( Stage::initial () )
	{
	loop_dev = Storage::GetDeviceName( "/dev/loop", loop_num );
	}

    if( Mode::test () && loop_dev == "")
	loop_dev = "/dev/loop1";

    if( part["delete"]:false || 
	part["type"]:`unknown == `extended ||
        !part["loop_fs"]:false ||
        part["noauto"]:false ||
        part["crypt"]:"none"=="none" ||
	mountPoint == "" || 
	mountPoint == "swap" )
	{
	return $[];
	}

    string used_fs_str = FileSystems::GetMountString( used_fs, "auto" );

    map ret = $[ "device"  : partitionName,
		 "loop_dev": loop_dev,
		 "mount"   : mountPoint,
		 "vfstype" : used_fs_str,
		 "opt1"    : part["crypt"]:"twofish",
		 "opt2"    : FileSystems::RemoveCryptOpts(part["fstopt"]:"noatime") ];

    if( size(ret["opt2"]:"")==0 )
	{
	ret["opt2"] = "noatime";
	}

    if( part["type"]:`none == `loop )
	{
	ret["device"] = part["fpath"]:"";
	ret["loopfile"] = true;
	}

    if( part["crtabline"]:0 != 0 )
	{
	ret["line"] = part["crtabline"]:0;
	}
    y2milestone( "loop_num:%1 ret=%2", loop_num, ret );
    return( ret );
    };


define void write_cryptotab (map<string,map> targets)
    ``{
    map crtab = $[];
    list<integer> dellines = [];
    list cryptotab = [];
    list<string> disks = maplist( string k, map e, targets, ``(k));
    if( find( string e, disks, ``(e=="/dev/loop")) != nil )
	{
	disks = filter( string e, disks, ``(e!="/dev/loop"));
	disks = add( disks, "/dev/loop" );
	}

    foreach( string targetdevice, disks,
	``{
	list<map> partitions = targets[targetdevice,"partitions"]:[];
	foreach (map partition, partitions,
	    ``{
	    map cryptotabentry = onepartition2cryptotab( partition, 
	                                                 size(crtab) );

	    if( size(cryptotabentry)>0 )
		{
		crtab[partition["mount"]:""] = cryptotabentry;
		}
	    });
	list<map> delpart = filter(map p, partitions, 
				   ``( p["crtabline"]:0>0 &&
				   ( p["delete"]:false ||
				     !p["loop_fs"]:false ||
				     p["noauto"]:false ||
				     p["crypt"]:"none"=="none" ||
				     (size(p["mount"]:"")==0&&
				      size(p["ori_mount"]:"")>0))));
	dellines = (list<integer>)union( dellines, 
	                  maplist( map p, delpart, ``(p["crtabline"]:0 )));
	});

    y2milestone( "crtab %1", crtab );
    list<map> crtablist = (list<map>)maplist( any k, any v, crtab, ``(v));

    string tabpath = Storage::PathToDestdir( "/etc/cryptotab" );
    if( Installation::scr_destdir != "/" )
	{
	if( SCR::Read( .target.size, tabpath )>=0 )
	    SCR::Execute( .target.remove, tabpath );
	}
    crtab = Partitions::GetCrypto( tabpath );

    // crtab is sorted by mountpoint
    y2milestone( "dellist=%1", dellines );
    y2milestone( "crtablist=%1", crtablist );

    foreach(map entry, crtablist,
	``{
	list fstlist = [ entry["loop_dev"]:"", entry["device"]:"", 
	                 entry["mount"]:"", entry["vfstype"]:"", 
			 entry["opt1"]:"", entry["opt2"]:"" ];
	if( entry["line"]:0 > 0 )
	    {
	    map part = Storage::GetPartition( targets, entry["device"]:"" );
	    if( entry["loopfile"]:false )
		{
		part = Storage::GetPartition( targets, entry["loop_dev"]:"" );
		}
	    y2milestone( "part %1", part );
	    if( part["format"]:false ||
		(haskey(part,"ori_mount") && 
		 part["ori_mount"]:""!=part["mount"]:"") ||
		(haskey(part,"ori_fstopt") && 
	         part["ori_fstopt"]:""!=part["fstopt"]:"") ||
		(haskey(part,"ori_nr") &&
	         part["ori_nr"]:""!=part["nr"]:"") ||
	        (part["mountby"]:`device==`label && haskey(part,"ori_label") &&
		 part["ori_label"]:"" != part["label"]:""))
		{
		integer field = 0;
		while( field<6 )
		    {
		    AsciiFile::ChangeLineField( crtab, entry["line"]:0, field, 
						fstlist[field]:"" );
		    field = field+1;
		    }
		}
	    }
	else
	    {
	    AsciiFile::AppendLine( crtab, fstlist );
	    }
	string dir = entry["mount"]:"";
	if( substring( dir, 0, 1 )== "/" )
	    {
	    if( Installation::scr_destdir != "/" )
		dir = Installation::scr_destdir + dir;
	    if( SCR::Read( .target.size, dir )<=0 )
		{
		SCR::Execute( .target.mkdir, dir );
		}
	    }
	});

    AsciiFile::RemoveLines( crtab, dellines );
    AsciiFile::RewriteFile( crtab, tabpath );
    };

//-----------------------------------------------------
//
// construct and write complete fstab
// return: string foreign_primary
//
define void write_fstab( map<string,map> & targetMap, map zipdrives, list<map> cddevices )
    ``{
    map<string,map> tg = targetMap;
    if( !Stage::initial ())
	{
	tg = Storage::AddFstabInfo( tg, true );
	}
    map<string,map> fstab = $[];
    list<integer> dellines = [];
    string dir = "";

    integer other_nr = 1;		// count other mounts

    foreach (string tdevice, map tdata, tg,
	``{
	list<map> partitions = tdata["partitions"]:[];
	allpartitions2fstab (fstab, partitions, other_nr);
	list<map> delpart = filter( map p, partitions, 
			       ``( p["fstabline"]:0>0 &&
				   ( p["delete"]:false ||
				     (p["loop_fs"]:false && 
				      !p["noauto"]:false && 
				      p["crypt"]:"none"!="none") ||
				     (size(p["mount"]:"")==0 &&
				      size(p["ori_mount"]:"")>0))));
	y2milestone( "partitions:%1", partitions );
	y2milestone( "delpart:%1", delpart );
	dellines = (list<integer>)union( dellines, 
			  maplist( map p, delpart, ``(p["fstabline"]:0 )));
	});

    list<map> fstablist = maplist (string k, map v, fstab, ``(v));
    fstab = $[];

    if( Stage::initial () )
	{
	// proc
	fstab["/proc"] = FileSystems::GetFstabDefaultMap( "proc" );
	// sysfs
	fstab["/sys"] = FileSystems::GetFstabDefaultMap( "sys" );

	// usbfs added with noauto
	if (Hotplug::haveUSB)
	    {
	    fstab["/proc/bus/usb"] = FileSystems::GetFstabDefaultMap( "usb" );
	    }

	// devpts
	fstab["/dev/pts"] = FileSystems::GetFstabDefaultMap( "pts" );
	fstablist = (list<map>)union( fstablist, maplist(string k, map v, fstab, ``(v)));
	fstab = $[];

	y2milestone( "zipdrives=%1", zipdrives );
	integer zipnum = 0;
	foreach (string tdevice, any tdata, (map<string,any>)zipdrives, 
	    ``{
	    string zfile = "/zip";
	    if (zipnum > 0)
		zfile = zfile + sformat("%1",zipnum);

	    string media = "/media"+zfile;
	    fstab[media] = FileSystems::GetFstabDefaultMap( "zip" );
	    fstab[media,"spec"] = tdevice+"4";
	    fstab[media,"mount"] = media;
	    zipnum = zipnum + 1;
	    });
	fstablist = (list<map>)union( fstablist, maplist (string k, map v, fstab, ``(v)));
	fstab = $[];

	y2milestone( "cddevices=%1", cddevices );

	foreach (map cd, (list<map>)cddevices, ``{
	    y2milestone( "cd %1", cd );
	    map ret = Storage::MakeCdromFstabEntry( cd );
	    fstab[ret["mount"]:""] = ret;
	    });
	/* disabled according to bug #36469 
	if( size(cddevices)>0 && !haskey( fstab, "/media/cdrom" ) )
	    {
	    map tm = $[];
	    list<string> names = (list<string>)StorageDevices::TypeNames;
	    foreach(string n, names, 
		``{
		string dir ="/media/" + n;
		if( size(tm)==0 && haskey( fstab, dir ) )
		    {
		    tm = fstab[dir]:$[];
		    }
		});
	    if( size(tm)>0 )
		{
		fstab["/media/cdrom"] = eval(tm);
		fstab["/media/cdrom","mount"] = "/media/cdrom";
		fstab["/media/cdrom","spec"] = "/dev/cdrom";
		}
	    }
	if( size(cddevices)>0 && !haskey( fstab, "/media/dvd" ) )
	    {
	    list<map> tcd = (list<map>) filter( map e, cddevices, ``(e["dvd"]:false));
	    y2milestone( "dvd cap %1", tcd );
	    if( size(tcd)>0 )
		{
		fstab["/media/dvd"] = Storage::MakeCdromFstabEntry( tcd[0]:$[] );
		fstab["/media/dvd","mount"] = "/media/dvd";
		fstab["/media/dvd","spec"] = "/dev/dvd";
		y2milestone( "dvd entry %1", fstab["/media/dvd"]:$[] );
		}
	    }
	if( size(cddevices)>0 && !haskey( fstab, "/media/cdrecorder" ) )
	    {
	    list<map> tcd = (list<map>) filter(map e, cddevices, ``(e["cdrw"]:false));
	    y2milestone( "dcr cap %1", tcd );
	    if( size(tcd)>0 )
		{
		fstab["/media/cdrecorder"] = Storage::MakeCdromFstabEntry( tcd[0]:$[] );
		fstab["/media/cdrecorder","mount"] = "/media/cdrecorder";
		fstab["/media/cdrecorder","spec"] = "/dev/cdrecorder";
		y2milestone( "cdr entry %1", fstab["/media/cdrecorder"]:$[] );
		}
	    }
	*/ 
	fstablist = (list<map>)union( fstablist, maplist (string k, map v, fstab, ``(v)));
	y2milestone( "fstablist %1", fstablist );
	fstab = $[];

	// handle all drives with /dev/fdX

	y2milestone( "FloppyDrives=%1", StorageDevices::FloppyDrives );

	integer fdnum = 0;
	foreach (map drive, StorageDevices::FloppyDrives,
	    ``{
	    string dev_name = drive["dev_name"]:"";
	    string fdname = "/floppy";
	    if (fdnum > 0)
		fdname = fdname + fdnum;
	    string medianame = "/media"+fdname;
	    fstab[medianame] = FileSystems::GetFstabDefaultMap( "floppy" );
	    fstab[medianame,"spec"] = dev_name;
	    fstab[medianame,"mount"] = medianame; 
	    fdnum = fdnum + 1;
	    });
	fstablist = (list<map>)union( fstablist, maplist (any k, any v, fstab, ``(v)));
	fstab = $[];
	}

    string tabpath = "/etc/fstab";
    if( Installation::scr_destdir != "/" )
	{
	tabpath = Installation::scr_destdir + tabpath;
	if( SCR::Read( .target.size, tabpath )>= 0 )
	    SCR::Execute( .target.remove, tabpath );
	}
    map fstabf = Partitions::GetFstab( tabpath );

    // fstab is sorted by mountpoint
    y2milestone( "dellist=%1", dellines );
    y2milestone( "fstablist=%1", fstablist );

    foreach(map entry, fstablist,
	``{
	list fstlist = [ entry["spec"]:"", entry["mount"]:"", 
	                 entry["vfstype"]:"", entry["mntops"]:"", 
			 sformat("%1",entry["freq"]:0),
			 sformat("%1",entry["passno"]:0) ];
	if( entry["line"]:0 > 0 )
	    {
	    map part = Storage::GetPartition( tg, entry["device"]:(entry["spec"]:"") );
	    if( entry["loopfile"]:false )
		{
		part = Storage::GetPartition( tg, entry["loop_dev"]:"" );
		}
	    if( size(part["mount"]:"")>0 && 
		contains( dellines, entry["line"]:0 ))
		{
		dellines = filter( integer e, dellines, ``(e!=entry["line"]:0));
		y2milestone( "dellines %1", dellines );
		}
	    if( part["format"]:false || 
	        (haskey(part,"ori_mountby") &&
	         part["mountby"]:`device!=part["ori_mountby"]:`device) ||
		(haskey(part,"ori_mount") && 
		 part["ori_mount"]:""!=part["mount"]:"") ||
		(haskey(part,"ori_fstopt") && 
	         part["ori_fstopt"]:""!=part["fstopt"]:"") ||
		(haskey(part,"ori_nr") &&
	         part["ori_nr"]:""!=part["nr"]:"") ||
	        (part["mountby"]:`device==`label && haskey(part,"ori_label") &&
		 part["ori_label"]:"" != part["label"]:""))
		{
		integer field = 0;
		while( field<4 )
		    {
		    AsciiFile::ChangeLineField( fstabf, entry["line"]:0, field, 
		                                fstlist[field]:"" );
		    field = field+1;
		    }
		}
	    }
	else
	    {
	    AsciiFile::AppendLine( fstabf, fstlist );
	    }
	dir = entry["mount"]:"";
	if( substring( dir, 0, 1 )== "/" )
	    {
	    if( Installation::scr_destdir != "/" )
		dir = Installation::scr_destdir + dir;
	    if( SCR::Read( .target.size, dir )<=0 )
		{
		SCR::Execute( .target.mkdir, dir );
		}
	    }
	});

    AsciiFile::RemoveLines( fstabf, dellines );
    if( Installation::scr_destdir != "/" )
	{
	dir = Installation::scr_destdir + "/etc";
	if( SCR::Read( .target.size, dir )<=0 )
	    {
	    SCR::Execute( .target.mkdir, dir );
	    }
	}
    AsciiFile::RewriteFile( fstabf, tabpath );

    write_cryptotab( tg );
    };

}	
