ACC SHELL

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

/*
 *************************************************************
 *
 *     YaST2      SuSE Labs                        -o)
 *     --------------------                        /\\
 *                                                _\_v
 *           www.suse.de / www.suse.com
 * ----------------------------------------------------------
 *
 * Author:        Thomas Fehr <fehr@suse.de>
 *
 * Description:   Make a proposal for partitioning
 *
 *************************************************************/
{
    module "StorageProposal";

    textdomain "storage";

    import "FileSystems";
    import "Partitions";
    import "Storage";
    import "ProductFeatures";
    import "Arch";
    import "Stage";


map read_partition_config( string fpath );
map read_partition_xml_config();
map get_gap_info( map disk, boolean add_exist_linux );
map add_cylinder_info( map conf, map gap );
map get_perfect_list( list ps, map g );
map process_partition_data( string dev, map solution, string vgname );
map find_matching_disk( list<string> disks, map target, map conf );
map try_resize_windows( map disk );
map remove_possible_partitions( map disk, map conf );
map distribute_space( integer rest, list weights, list added, list ps );
void add_part_recursive( list ps, map g );
map normalize_gaps( list ps, map g );
integer do_weighting( list ps, map g );

symbol cur_mode = `free;
integer cur_weight = -10000;
map cur_gap = $[];
integer big_cyl = 4 * 1024 * 1024 * 1024;

    list<string> no_propose_disks = nil;

    boolean proposal_home = false;
    boolean proposal_lvm = false;
    boolean proposal_encrypt = false;
    string proposal_password = "";

    map<string, any> cfg_xml = $[];


    global boolean GetProposalHome()
    {
	return proposal_home;
    }

    global void SetProposalHome(boolean val)
    {
	proposal_home = val;
	y2milestone("SetProposalHome val:%1", proposal_home);
    }

    global boolean GetProposalLvm()
    {
	return proposal_lvm;
    }

    global void SetProposalLvm(boolean val)
    {
	proposal_lvm = val;
	y2milestone("SetProposalLvm val:%1", val);
    }

    global boolean GetProposalEncrypt()
    {
	return proposal_encrypt;
    }

    global void SetProposalEncrypt(boolean val)
    {
	proposal_encrypt = val;
	y2milestone("SetProposalEncrypt val:%1", val);
    }

    global string GetProposalPassword()
    {
	return proposal_password;
    }

    global void SetProposalPassword(string val)
    {
	proposal_password = val;
	y2milestone("SetProposalPassword");
    }

    global void SetProposalDefault(boolean home_only)
    {
	SetProposalHome(cfg_xml["home"]:false);
	if (!home_only)
	{
	    SetProposalLvm(cfg_xml["prop_lvm"]:false);
	    SetProposalEncrypt(false);
	    SetProposalPassword("");
	}
	y2milestone("SetProposalDefault home:%1 lvm:%2 encypt:%3 home_only:%4", proposal_home,
		    proposal_lvm, proposal_encrypt, home_only);
    }


    global map<string, any> GetControlCfg()
    {
	if (isempty(cfg_xml))
	{
	    boolean btmp = ProductFeatures::GetBooleanFeature("partitioning", "try_separate_home");
	    cfg_xml["home"] = btmp;

	    integer itmp = ProductFeatures::GetIntegerFeature("partitioning", "root_space_percent");
	    cfg_xml["root_percent"] = itmp;
	    if (cfg_xml["root_percent"]:0 == nil || cfg_xml["root_percent"]:0 <= 0)
		cfg_xml["root_percent"] = 40;

	    string stmp = ProductFeatures::GetStringFeature("partitioning", "limit_try_home");
	    cfg_xml["home_limit"] = Storage::ClassicStringToByte(stmp) / (1024*1024);
	    if (cfg_xml["home_limit"]:0 <= 0)
		cfg_xml["home_limit"] = 5*1024;

	    stmp = ProductFeatures::GetStringFeature("partitioning", "root_base_size");
	    cfg_xml["root_base"] = Storage::ClassicStringToByte(stmp) / (1024*1024);
	    if (cfg_xml["root_base"]:0 <= 0)
		cfg_xml["root_base"] = 3*1024;

	    stmp = ProductFeatures::GetStringFeature("partitioning", "root_max_size");
	    cfg_xml["root_max"] = Storage::ClassicStringToByte(stmp) / (1024*1024);
	    if (cfg_xml["root_max"]:0 <= 0)
		cfg_xml["root_max"] = 10*1024;

	    btmp = ProductFeatures::GetBooleanFeature("partitioning", "vm_keep_unpartitioned_region");
	    cfg_xml["vm_keep_unpartitioned_region"] = btmp;

	    stmp = ProductFeatures::GetStringFeature("partitioning", "vm_desired_size");
	    cfg_xml["vm_want"] = Storage::ClassicStringToByte(stmp) / (1024*1024);
	    if (cfg_xml["vm_want"]:0 <= 0)
		cfg_xml["vm_want"] = 15*1024;

	    stmp = ProductFeatures::GetStringFeature("partitioning", "vm_home_max_size");
	    cfg_xml["home_max"] = Storage::ClassicStringToByte(stmp) / (1024*1024);
	    if (cfg_xml["home_max"]:0 <= 0)
		cfg_xml["home_max"] = 25*1024;

	    btmp = ProductFeatures::GetBooleanFeature("partitioning", "proposal_lvm");
	    cfg_xml["prop_lvm"] = btmp ? true : false;

	    SetProposalDefault(false);
	    y2milestone("GetControlCfg cfg_xml:%1", cfg_xml);
	}

	return cfg_xml;
    }


    global string GetProposalVM()
    {
	string ret = "";
	if (proposal_lvm)
	{
	    ret = "system";
	}
	y2milestone("ProposalVM lvm:%1 ret:%2", proposal_lvm, ret);
	return ret;
    }


    map<string, map> EncryptDevices(map<string, map> target, string vg)
    {
	if (!GetProposalLvm() || !GetProposalEncrypt())
	    return target;

	list<string> devices = target[vg, "devices_add"]:[];

	y2milestone("vg:%1 devices:%2", vg, devices);

	// go through target map and set enc_type and password for all devices used by
	// our volume group
	target = mapmap(string disk_device, map data, target, {
	    list<map> partitions = data["partitions"]:[];
	    partitions = maplist(map partition, partitions, {
		string part_device = partition["device"]:"";
		if (contains(devices, part_device))
		{
		    y2debug("setting encryption for %1", part_device);
		    partition["enc_type"] = `luks;
		    Storage::SetCryptPwd(part_device, proposal_password);
		}
		return partition;
	    });
	    data["partitions"] = partitions;
	    return $[ disk_device : data ];
	});

	y2milestone("target:%1", target);

	return target;
    }


    list<string> GetDestroyedLvmVgs(map<string, map> target)
    {
	list<string> vgs = [];
	foreach(string diskdev, map disk, target, {
	    foreach( map p, disk["partitions"]:[], {
		if( p["used_by_type"]:`UB_NONE==`UB_LVM && p["format"]:false )
		{
		    vgs = add(vgs, p["used_by_device"]:"");
		}
	    });
	});
	vgs = toset(vgs);
	y2milestone( "GetDestroyedLvmVgs %1", vgs );
	return vgs;
    }


    map<string, map> DeleteDestroyedLvmVgs(map<string, map> target)
    {
	list<string> vgs = GetDestroyedLvmVgs(target);
	y2milestone( "DeleteDestroyedLvmVgs %1", vgs );
	foreach(string dev, vgs, {
	    if( haskey( target, dev ))
		target[dev,"delete"] = true;
	    target[dev,"partitions"] = maplist( map p, target[dev,"partitions"]:[],
						``{
						    if( haskey( p, "mount" ))
							p = remove( p, "mount" );
						    return( p );
						});
	    y2milestone( "DeleteDestroyedLvmVgs %1: %2", dev, target[dev]:$[] );
	});
	return target;
    }


    list<string> NoProposeDisks()
    {
	if (no_propose_disks == nil)
	{
	    no_propose_disks = [];
	    if( Stage::initial() && SCR::Read( .target.size, "/etc/install.inf" )>0 )
	    {
		string inst = (string) SCR::Read( .etc.install_inf.Partition );
		y2milestone( "NoProposeDisks .etc.install_inf.Partition \"%1\"",
			     inst );
		if( inst!=nil && size(inst)>0 )
                {
		    if( search( inst, "/dev/" )!=0 )
			inst = "/dev/" + inst;
		    map d = Storage::GetDiskPartition( inst );
		    y2milestone( "NoProposeDisks inst:%1 disk:%2", inst, d );
		    if( size(d["disk"]:"")>0 )
			no_propose_disks = add( no_propose_disks, d["disk"]:"" );
                }
		inst = (string) SCR::Read( .etc.install_inf.Cdrom );
		y2milestone( "NoProposeDisks .etc.install_inf.Cdrom \"%1\"", inst );
		if( inst!=nil && size(inst)>0 )
		{
		    if( search( inst, "/dev/" )!=0 )
			inst = "/dev/" + inst;
		    map d = Storage::GetDiskPartition( inst );
		    y2milestone( "NoProposeDisks inst:%1 disk:%2", inst, d );
		    if( size(d["disk"]:"")>0 )
			no_propose_disks = add( no_propose_disks, d["disk"]:"" );
		}
	    }
	    if( Stage::initial() )
	    {
		string env = getenv("YAST2_STORAGE_NO_PROPOSE_DISKS");
		y2milestone( "NoProposeDisks env:\"%1\"", env );
		if (env != nil)
		{
		    list<string> ls = filter( string e, splitstring( env, " \t\n" ), ``(size(e)>0) );
		    no_propose_disks = (list<string>)merge( no_propose_disks, ls );
		}
	    }
	    no_propose_disks = toset(sort(no_propose_disks));
	    y2milestone( "NoProposeDisks \"%1\"", no_propose_disks );
	}
	return no_propose_disks;
    }


    boolean ignore_disk(string dev, map entry, boolean soft)
    {
    boolean ret = !Storage::IsPartitionable(entry) || entry["readonly"]:false;
    // GPT is not required for uEFI on x86_64
    if( !ret && Arch::ia64() && entry["label"]:"gpt"!="gpt" )
	{
	ret = true;
	}
    if( !ret && soft && Arch::board_iseries () && search( dev, "/dev/sd" )==0 )
	{
	ret = true;
	}
    if( !ret && soft && (entry["softraiddisk"]:false||entry["hotpluggable"]:false) )
	{
	ret = true;
	}
    if( !ret && soft && contains( NoProposeDisks(), dev ))
	{
	ret = true;
	}
    if( !ret && soft )
	{
	ret = entry["used_by_type"]:`UB_NONE != `UB_NONE;
	}
    if( ret )
	{
	y2milestone( "ignoring disk %1 soft:%2", dev, soft );
	}
    return( ret );
    }


    map<string, map> AddWinInfo(map<string, map> targets)
    {
	y2milestone("AddWinInfo called");
	foreach(string disk, map data, targets, {
	    targets[disk, "partitions"] =
		maplist(map p, data["partitions"]:[], {
		    if (Partitions::IsDosWinNtPartition(p["fsid"]:0) &&
			contains([ `ntfs, `vfat ], p["used_fs"]:`none))
		    {
			p["winfo"] = Storage::GetFreeSpace(p["device"]:"",
							   p["used_fs"]:`none, false);
			y2milestone("AddWinInfo %1", p);
		    }
		    return p;
		});
	});
	return targets;
    }


map<string, boolean> swapable = $[];


    global void flex_init_swapable(map<string, map> tg)
    {
    swapable = $[];
    list<string> swaps = Storage::SwappingPartitions();
    foreach(string dev, map disk, tg, {
	if (disk["used_by_type"]:`UB_NONE == `UB_NONE)
	{
	    list<map> sw = filter(map p, disk["partitions"]:[],
				  ``(p["type"]:`unknown!=`extended &&
				     !p["delete"]:false &&
				     p["detected_fs"]:`unknown==`swap));
	    sw = filter(map p, sw, ``(contains(swaps, p["device"]:"") ||
				      Storage::CheckSwapable(p["device"]:"")));
	    foreach(map p, sw, {
		swapable[p["device"]:""] = true;
	    });
	}
    });
    y2milestone("flex_init_swapable %1", swapable);
    }


    boolean check_swapable(string dev)
    {
	return swapable[dev]:false;
    }


    string pinfo_name()
    {
	return "/part.info";
    }


global boolean has_flex_proposal()
    {
    boolean ret = (integer)SCR::Read( .target.size, pinfo_name() )>0;
    if( !ret )
	{
	any t = ProductFeatures::GetBooleanFeature( "partitioning", 
						    "use_flexible_partitioning");
	y2milestone( "ProductFeatures::GetBooleanFeature %1", t );
	if( is( t, boolean ) && (boolean)t )
	    ret = true;
	}
    y2milestone( "has_flex_proposal ret:%1", ret );
    return( ret );
    }


boolean need_boot( map disk )
    {
    return( Partitions::NeedBoot() ||
	    disk["type"]:`CT_UNKNOWN == `CT_DMRAID );
    }


map try_add_boot( map conf, map disk, boolean force )
    {
    boolean boot = 
	size(filter( map e, conf["partitions"]:[], 
		     ``(e["mount"]:""==Partitions::BootMount()))) > 0;
    boolean root = size(filter( map e, conf["partitions"]:[], 
				``(e["mount"]:""=="/"))) > 0;
    map tc = conf;
    y2milestone( "try_add_boot conf %1", conf );
    y2milestone( "try_add_boot boot %1 root %2 force %3", boot, root, force );
    if( !boot && (root||force) && 
        (disk["cyl_count"]:0>Partitions::BootCyl() || need_boot(disk)) )
	{
	map pb = $[];
	pb["mount"] = Partitions::BootMount();
	pb["size"] = Partitions::MinimalNeededBootsize();
	pb["fsys"] = Partitions::DefaultBootFs();
	pb["id"] = Partitions::FsidBoot();
	pb["auto_added"] = true;
	pb["max_cyl"] = Partitions::BootCyl();
	pb["primary"] = Partitions::BootPrimary();
	pb["maxsize"] = 500*1024*1024;
	tc["partitions"] = add( tc["partitions"]:[], pb );
	y2milestone( "try_add_boot disk_cyl %1 boot_cyl %2 need_boot %3 typ %4",
	             disk["cyl_count"]:0, Partitions::BootCyl(), 
		     Partitions::NeedBoot(), disk["type"]:`CT_UNKNOWN );
	y2milestone( "try_add_boot boot added automagically pb %1", pb );
	}
    return( tc );
    }


global map<string, any> do_flexible_disk(map disk)
    {
    string dev = disk["device"]:"";
    y2milestone( "do_flexible_disk dev %1", dev );
    y2milestone( "do_flexible_disk parts %1", disk["partitions"]:[] );
    map<string, any> ret = $[];
    ret["ok"] = false;
    map conf = read_partition_config( pinfo_name() );
    map solutions = $[];

    if( size(conf)>0 && Storage::IsPartitionable( disk ))
	{
	y2milestone( "do_flexible_disk processing disk %1", dev );
	map tc = try_add_boot( conf, disk, false );
	cur_mode = `free;
	cur_gap = $[];
	map gap = get_gap_info( disk, false );
	tc = add_cylinder_info( tc, gap );
	map sol = get_perfect_list( tc["partitions"]:[], gap );
	if( size(sol)>0 )
	    {
	    sol["disk"] = eval(disk);
	    ret["ok"] = true;
	    ret["disk"] = process_partition_data( dev, sol, "" );
	    ret["weight"] = sol["weight"]:-1;
	    }
	}
    y2milestone( "do_flexible_disk ret %1", ret["ok"]:false );
    if( ret["ok"]:false )
	{
	y2milestone( "do_flexible_disk weight %1", ret["weight"]:-2 );
	y2milestone( "do_flexible_disk disk %1", ret["disk"]:$[] );
	}
    return( ret );
    }


map do_flexible_disk_conf( map disk, map co, boolean ignore_boot,
                                  boolean reuse )
    {
    string dev = disk["device"]:"";
    y2milestone( "do_flexible_disk_conf dev %1 ignore_boot %2 reuse %3", 
                 dev, ignore_boot, reuse );
    map conf = co;
    if( !ignore_boot )
	conf = try_add_boot( conf, disk, true );
    y2milestone( "do_flexible_disk_conf parts %1", disk["partitions"]:[] );
    y2milestone( "do_flexible_disk_conf conf %1", conf );
    map ret = $[];
    ret["ok"] = false;
    map solutions = $[];

    if( size(conf)>0 && size(conf["partitions"]:[])>0 && 
        Storage::IsPartitionable( disk ))
	{
	y2milestone( "do_flexible_disk_conf processing disk %1", dev );
	cur_mode = reuse?`reuse:`free;
	cur_gap = $[];
	map gap = get_gap_info( disk, reuse );
	map tc = add_cylinder_info( conf, gap );
	map sol = get_perfect_list( tc["partitions"]:[], gap );
	if( size(sol)>0 )
	    {
	    sol["disk"] = eval(disk);
	    ret["ok"] = true;
	    ret["disk"] = process_partition_data( dev, sol, "" );
	    ret["weight"] = sol["weight"]:-1;
	    }
	}
    else if( Storage::IsPartitionable( disk ) )
	{
	ret["ok"] = true;
	ret["disk"] = disk;
	}
    y2milestone( "do_flexible_disk_conf ret %1", ret["ok"]:false );
    if( ret["ok"]:false && size(conf)>0 )
	{
	y2milestone( "do_flexible_disk_conf weight %1", ret["weight"]:-2 );
	y2milestone( "do_flexible_disk_conf parts %1", ret["disk","partitions"]:[] );
	}
    return( ret );
    }


map do_vm_disk_conf( map disk, map boot, string vmkey, string key )
    {
    string dev = disk["device"]:"";
    y2milestone( "do_vm_disk_conf dev %1 vmkey %2 key %3 boot %4", 
                 dev, vmkey, key, boot );
    y2milestone( "do_vm_disk_conf parts %1", disk["partitions"]:[] );
    map conf = $[ "partitions" : [ $[ "id" : 0x8E ] ] ];
    if( size(boot)>0 )
	conf["partitions"] = add( conf["partitions"]:[], boot );
    map ret = $[];
    ret["ok"] = false;

    integer fsid = conf["partitions",0,"id"]:0;
    if( Storage::IsPartitionable( disk ))
	{
	y2milestone( "do_vm_disk_conf processing disk %1", dev );
	cur_mode = `free;
	cur_gap = $[];
	map gap = get_gap_info( disk, true );
	y2milestone( "do_vm_disk_conf gap %1", gap );
	y2milestone( "do_vm_disk_conf conf %1", conf );
	map tc = add_cylinder_info( conf, gap );
	y2milestone( "do_vm_disk_conf tc %1", tc );
	y2milestone( "do_vm_disk_conf gap %1", gap["gap"]:[] );
	if( size(gap["gap"]:[])>1 )
	    {
	    gap["gap"] = sort( map a, map b, gap["gap"]:[],
		``{
		if( a["extended"]:false==b["extended"]:false )
		    return( a["start"]:0<b["start"]:0 );
		else
		    return( !a["extended"]:false );
		});
	    y2milestone( "do_vm_disk_conf gap %1", gap["gap"]:[] );
	    }
	boolean ok = size(boot)==0;
	if( !ok )
	    {
	    integer gstart = -1;
	    map bo = find( map p, (list<map>)tc["partitions"]:[], 
	                   ``(p["mount"]:""==boot["mount"]:"") );
	    y2milestone( "do_vm_disk_conf boot %1", bo );
	    integer cyl_num = bo["cylinders"]:1;
	    y2milestone( "do_vm_disk_conf boot cyl %1", cyl_num );
	    gap["gap"] = maplist( map g, gap["gap"]:[],
		``{
		if( !g["exists"]:false && !ok && g["cylinders"]:0>=cyl_num &&
		    ((g["extended"]:false && size(gap["ext_pnr"]:[])>1)||
		     (!g["extended"]:false && size(gap["free_pnr"]:[])>1)))
		    {
		    ok = true;
		    gstart = g["start"]:-1;
		    string key = g["extended"]:false ? "ext_pnr" : "free_pnr";
		    g["added"] = [ [ 1, gap[key,0]:0, cyl_num ] ];
		    g["cylinders"] = g["cylinders"]:0-cyl_num;
		    gap[key] = remove( gap[key]:[], 0 );
		    if( g["cylinders"]:0 > 1 )
			{
		        integer vg_size = GetControlCfg()["vm_want"]:(15*1024);
		        // get only limited amount of the gap so that there is free
		        // space left (fate #303594)
		        y2milestone ("do_vm_disk_conf maximum volume group size from control file: %1", vg_size);
			integer add_cylinders = g["cylinders"]:0;  // whole partition as default
			if (vg_size != nil && vg_size > 0 && GetControlCfg()["vm_keep_unpartitioned_region"]:false)
			{
			    integer cyl_size = g["size"]:0 / g["cylinders"]:0;
			    add_cylinders = (vg_size * 1024 * 1024) / cyl_size;
			    if (add_cylinders > g["cylinders"]:0)
				add_cylinders = g["cylinders"]:0;
			}
			g["added"] = add( g["added"]:[],
			                  [ 0, gap[key,0]:0, add_cylinders ] );
		        y2milestone ("do_vm_disk_conf added new partition: %1", g);
			g["cylinders"] = 0; // do not use the rest of the gap, if not set to zero, it is used, don't know why
			gap[key] = remove( gap[key]:[], 0 );
			}
		    }
		return( g );
		});
	    if( !ok )
		{
		gap["gap"] = maplist( map g, gap["gap"]:[],
		    ``{
		    if( !g["exists"]:false && !ok && g["cylinders"]:0>=cyl_num &&
			((g["extended"]:false && size(gap["ext_pnr"]:[])>0)||
			 (!g["extended"]:false && size(gap["free_pnr"]:[])>0)))
			{
			ok = true;
			gstart = g["start"]:-1;
			string key = g["extended"]:false ? "ext_pnr" : "free_pnr";
			g["added"] = [ [ 1, gap[key,0]:0, cyl_num ] ];
			g["cylinders"] = g["cylinders"]:0-cyl_num;
			gap[key] = remove( gap[key]:[], 0 );
			}
		    return( g );
		    });
		}
	    if( ok && size(vmkey)>0 )
		{
		gap["gap"] = filter( map g, gap["gap"]:[], 
		                     ``(g["start"]:0==gstart));
		}
	    y2milestone( "do_vm_disk_conf gap %1", gap["gap"]:[] );
	    }
	else if( size(vmkey)>0 )
	    gap = $[];
	map sol = $[];
	sol["solution"] = gap;
	sol["partitions"] = conf["partitions"]:[];
	sol["disk"] = disk;
	ret["ok"] = ok;
	ret["weight"] = 0;
	if( ok && size(vmkey)==0 )
	    {
	    if( size(gap["ext_reg"]:[])>0 )
		{
		integer ext_end = gap["ext_reg",0]:0 + gap["ext_reg",1]:0 - 1;
		map aext = find( map g, gap["gap"]:[], 
		                 ``(size(g["added"]:[])==0 &&
				    !g["exists"]:false &&
				    g["start"]:0>=ext_end &&
				    g["start"]:0-ext_end<=1));
		y2milestone( "do_vm_disk_conf ee:%1 ae:%2", ext_end, aext );
		if( aext!=nil )
		    {
		    gap["resize_ext"] = aext["end"]:0;
		    gap["gap"] = filter( map g, gap["gap"]:[],
		                         ``(g["start"]:0!=aext["start"]:0));
		    aext = find( map g, gap["gap"]:[],
				 ``(!g["exists"]:false && g["extended"]:false &&
				    g["end"]:0==ext_end));
		    y2milestone( "do_vm_disk_conf aext %1", aext );
		    if( aext != nil )
			{
			gap["gap"] = maplist( map g, gap["gap"]:[],
			    ``{
			    if( g["end"]:0==aext["end"]:0 )
				{
				g["cylinders"] = g["cylinders"]:0 + 
				                 gap["resize_ext"]:0 -
						 g["end"]:0;
				g["end"] = gap["resize_ext"]:0;
				}
			    return( g );
			    });
			}
		    else
			{
			map a = $[ "extended" : true,
				   "start" : gap["ext_reg",0]:0+gap["ext_reg",1]:0,
			           "end" : gap["resize_ext"]:0 ];
			a["cylinders"] = a["end"]:0 - a["start"]:0 + 1;
		        y2milestone("do_vm_disk_conf add gap %1", a ); 
		        y2milestone("do_vm_disk_conf add gap %1", gap["gap"]:[] ); 
			gap["gap"] = add( gap["gap"]:[], a );
		        y2milestone("do_vm_disk_conf add gap %1", gap["gap"]:[] ); 
			}
		    }
		y2milestone( "do_vm_disk_conf aext gap %1", gap );
		}
	    gap["gap"] = maplist( map g, gap["gap"]:[],
		``{
		if( g["exists"]:false )
		    {
		    map acur = find( map gg, gap["gap"]:[],
				     ``(size(gg["added"]:[])==0 &&
					!gg["exists"]:false &&
				        gg["extended"]:false==g["extended"]:false &&
				        gg["start"]:0>=g["end"]:0 && 
				        gg["start"]:0-g["end"]:0<=1));
		    y2milestone( "do_vm_disk_conf ee:%1 ae:%2", 
		                 g["end"]:0, acur );
		    if( acur!=nil )
			{
			g["resize"] = acur["end"]:0;
			g["fsid"] = fsid;
			}
		    }
		return( g );
		});
	    list<integer> sl = maplist( map g, gap["gap"]:[], 
	                                ``(g["resize"]:-1));
	    y2milestone( "do_vm_disk_conf sl %1", sl );
	    gap["gap"] = filter( map g, gap["gap"]:[],
				 ``(!contains(sl,g["end"]:0)));
	    gap["gap"] = sort( map a, map b, gap["gap"]:[],
		``{
		return( a["cylinders"]:0>b["cylinders"]:0 );
		});
	    y2milestone( "do_vm_disk_conf sorted gap %1", gap["gap"]:[] );
	    gap["gap"] = maplist( map g, gap["gap"]:[],
		``{
		if( !g["exists"]:false && g["cylinders"]:0>0 &&
		    ((g["extended"]:false && size(gap["ext_pnr"]:[])>0)||
		     (!g["extended"]:false && size(gap["free_pnr"]:[])>0)))
		    {
		    string key = g["extended"]:false ? "ext_pnr" : "free_pnr";
		    // get only limited amount of the gap so that there is free
		    // space left (fate #303594)
		    integer vg_size = GetControlCfg()["vm_want"]:(15*1024);
		    y2milestone ("do_vm_disk_conf maximum volume group size from control file: %1", vg_size);
		    integer add_cylinders = g["cylinders"]:0; // whole partition as default
		    if (vg_size != nil && vg_size > 0 && GetControlCfg()["vm_keep_unpartitioned_region"]:false)
		    {
		        integer cyl_size = g["size"]:0 / g["cylinders"]:0;
		        add_cylinders = (vg_size * 1024 * 1024) / cyl_size;
		        if (add_cylinders > g["cylinders"]:0)
			    add_cylinders = g["cylinders"]:0;
		    }
		    g["added"] = add( g["added"]:[], 
		                      [ 0, gap[key,0]:0, add_cylinders ]);
		    y2milestone ("do_vm_disk_conf added new partition: %1", g);
		    g["cylinders"] = 0; // otherwise it get another chunk - don't know why :-(
		    gap[key] = remove( gap[key]:[], 0 );
		    }
		return( g );
		});
	    gap["gap"] = maplist( map g, gap["gap"]:[],
		``{
		if( g["exists"]:false && fsid!=0 && g["fsid"]:0!=fsid )
		    {
		    g["fsid"] = fsid;
		    }
		return( g );
		});
	    y2milestone( "do_vm_disk_conf end gap %1", gap["gap"]:[] );
	    sol["solution"] = gap;
	    }
	ret["disk"] = process_partition_data( dev, sol, key );
	}
    y2milestone( "do_vm_disk_conf ret %1", ret["ok"]:false );
    if( ret["ok"]:false )
	{
	y2milestone( "do_vm_disk_conf weight %1", ret["weight"]:-2 );
	y2milestone( "do_vm_disk_conf parts %1", ret["disk","partitions"]:[] );
	}
    return( ret );
    }


list<string> restrict_disk_names(list<string> disks)
{
    void helper(string s)
    {
	integer count = 0;
	disks = filter(string dist, disks, {
	    if (search(dist, s) != 0)
		return true;
	    count = count + 1;
	    return count <= 16;
	});
    }

    helper("/dev/sd");
    helper("/dev/hd");
    helper("/dev/cciss/");
    helper("/dev/dasd");

    y2milestone("restrict_disk_names: ret %1", disks);
    return disks;
}


    map<string, any> do_pflex(map<string, map> target, map conf)
    {
    map<string, any> ret = $[];
    ret["ok"] = false;
    list<map> solutions = [];
    cur_mode = `free;
    if( size(conf)>0 )
	{
	list<string> ddev = maplist( string k, map e, 
	                     filter( string l, map f, target, 
			             ``(!ignore_disk(l,f,false))), ``(k));
	ddev = sort( ddev );
	y2milestone( "do_pflex ddev %1", ddev );
	map tc = $[];
	map<integer,any> dtmp = $[];
	foreach( map p, conf["partitions"]:[], 
	    ``{
	    integer dprio = p["disk"]:0;
	    if( haskey( dtmp, dprio ))
		{
		dtmp[dprio] = add( dtmp[dprio]:[], p );
		}
	    else
		{
		dtmp[dprio] = [ p ];
		}
	    });
	y2milestone( "do_pflex dlist %1", dtmp );
	list dlist = maplist( integer k, any e, dtmp, ``(e) );
	y2milestone( "do_pflex dlist %1", dlist );
	if( size(dlist)>size(ddev) )
	    {
	    integer idx = size(ddev);
	    while( idx<size(dlist) )
		{
		dlist[size(ddev)-1] = union( dlist[size(ddev)-1]:[], 
		                             dlist[idx]:[] );
		idx = idx+1;
		}
	    while( size(dlist)>size(ddev) )
		{
		dlist = remove( dlist, size(ddev) );
		}
	    y2milestone( "do_pflex dlist %1", dlist );
	    }
	list save_dlist = (list) eval(dlist);
	repeat
	    {
	    integer count = 0;
	    repeat
		{
		list<string> td = eval(ddev);
		integer idx = 0;
		y2milestone( "do_pflex start while count %1", count );
		while( idx<size(dlist) && count<size(dlist) )
		    {
		    y2milestone( "do_pflex in while idx %1", idx );
		    tc = (map) eval(conf);
		    tc["partitions"] = eval( dlist[idx]:[] );
		    map md = find_matching_disk( td, target, tc );
		    y2milestone( "do_pflex size(md) %1", size(md) );
		    if( size(md)>0 )
			{
			solutions = add( solutions, md );
			td = filter( string e, td, ``(e!=md["device"]:""));
			y2milestone( "do_pflex new td %1", td );
			idx = idx+1;
			}
		    else
			{
			y2milestone( "do_pflex no solution" );
			idx = size(dlist);
			td = eval(ddev);
			solutions = [];
			count = count + 1;
			if( size(dlist)>1 )
			    {
			    list tfi = dlist[0]:[];
			    dlist = remove( dlist, 0 );
			    dlist = add( dlist, tfi );
			    y2milestone( "do_pflex new rot dlist %1", dlist );
			    }
			}
		    }
		}
	    until( size(solutions)>0 || count>=size(dlist) );
	    if( size(solutions)==0 && size(dlist)>1 )
		{
		dlist = (list) eval(save_dlist);
		dlist[size(dlist)-2] = union( dlist[size(dlist)-2]:[],
					      dlist[size(dlist)-1]:[] );
		dlist = remove( dlist, size(dlist)-1 );
		y2milestone( "do_pflex new truncated dlist %1",
		             dlist );
		save_dlist = (list)eval(dlist);
		}
	    }
	until( size(solutions)>0 || size(dlist)<=1 );
	if( size(solutions)==0 &&
	    (size(conf["keep_partition_fsys"]:[])>0 ||
	     size(conf["keep_partition_id"]:[])>0 ||
	     size(conf["keep_partition_num"]:[])>0 ||
	     !conf["prefer_remove"]:false))
	    {
	    y2milestone( "do_pflex desperate mode" );
	    tc = (map) eval(conf);
	    cur_mode = `desparate;
	    tc["keep_partition_fsys"] = [];
	    tc["keep_partition_id"] = [];
	    tc["keep_partition_num"] = [];
	    tc["prefer_remove"] = true;
	    map md = find_matching_disk( ddev, target, tc );
	    if( size(md)>0 )
		{
		solutions = add( solutions, md );
		}
	    }
	if( size(solutions)>0 )
	    {
	    foreach( map e, solutions,
		``{
		string disk = e["device"]:"";
		target[disk] = process_partition_data( disk, e, "" );
		y2milestone( "do_pflex solution disk %1 %2", 
		             disk, target[disk]:$[] );
		});
	    ret["ok"] = true;
	    target = Storage::SpecialBootHandling( target );
	    ret["target"] = DeleteDestroyedLvmVgs(target);
	    }
	}
    return( ret );
    }


    map<string, any> do_proposal_flexible(map<string, map> target)
    {
    map conf = $[];
    if( ProductFeatures::GetBooleanFeature( "partitioning", 
                                            "use_flexible_partitioning"))
        conf = read_partition_xml_config();
    else
        conf = read_partition_config( pinfo_name() );
    y2milestone( "conf:%1", conf );
    return( do_pflex( target, conf ) );
    }


map find_matching_disk( list<string> disks, map target, map conf )
    {
    map<string,map> solutions = $[];

    cur_weight = -100000;
    cur_gap = $[];
    foreach( string k, disks,
	``{
	map e = target[k]:$[];
	y2milestone( "find_matching_disk processing disk %1", k );
	y2milestone( "find_matching_disk parts %1", conf["partitions"]:[] );
	map tc = try_add_boot( conf, e, false );
	if( cur_mode != `desparate )
	    cur_mode = `free;
	if( !tc["prefer_remove"]:false )
	    {
	    map gap = get_gap_info( e, false );
	    tc = add_cylinder_info( tc, gap );
	    map l = get_perfect_list( tc["partitions"]:[], gap );
	    if( size(l)>0 )
		{
		solutions[k] = eval(l);
		solutions[k,"disk"] = eval(e);
		}
	    cur_mode = `reuse;
	    map egap = get_gap_info( e, true );
	    if( size(egap["gap"]:[]) > size(gap["gap"]:[]) )
		{
		tc = add_cylinder_info( tc, egap );
		l = get_perfect_list( tc["partitions"]:[], egap );
		if( size(l)>0 && 
		    (!haskey(solutions,k) || 
		     (haskey( l, "weight" ) && 
		      l["weigth"]:0 > solutions[k,"weigth"]:0 )))
		    {
		    y2milestone( "find_matching_disk solution reuse existing" );
		    solutions[k] = eval(l);
		    solutions[k,"disk"] = eval(e);
		    }
		}
	    cur_mode = `resize;
	    map rw = try_resize_windows( e );
	    if( find( map p, rw["partitions"]:[], ``(p["resize"]:false))!=nil )
		{
		egap = get_gap_info( rw, true );
		tc = add_cylinder_info( tc, egap );
		l = get_perfect_list( tc["partitions"]:[], egap );
		if( size(l)>0 && 
		    (!haskey(solutions,k) ||
		     (haskey( l, "weight" ) && 
		      l["weigth"]:0 > solutions[k,"weigth"]:0 )))
		    {
		    y2milestone( "find_matching_disk solution resizing windows" );
		    solutions[k] = eval(l);
		    solutions[k,"disk"] = eval(rw);
		    }
		}
	    }
	else
	    {
	    map rp = remove_possible_partitions( e, tc );
	    map gap = get_gap_info( rp, false );
	    tc = add_cylinder_info( tc, gap );
	    map l = get_perfect_list( tc["partitions"]:[], gap );
	    if( size(l)>0 )
		{
		solutions[k] = eval(l);
		solutions[k,"disk"] = eval(rp);
		}
	    }
	});
    map ret = $[];
    if( size(solutions)>0 )
	{
	foreach( string k, map e, solutions,
	    ``{
	    y2milestone( "find_matching_disk disk %1 weight %2", 
	                 k, e["weight"]:0 );
	    });
	list<string> disks = maplist( string k, map e, solutions, ``(k) );
	disks = sort( string a, string b, disks, 
		      ``(solutions[a,"weight"]:0>solutions[b,"weight"]:0));
	y2milestone( "find_matching_disk sorted disks %1", disks );
	ret = solutions[disks[0]:""]:$[];
	ret["device"] = disks[0]:"";
	}
    return( ret );
    }


map process_partition_data( string dev, map solution, string vgname )
    {
    map disk = solution["disk"]:$[];
    list<map> partitions = [];
    string value = "";
    boolean remove_boot = false;
    if( size( filter( map e, solution["partitions"]:[], 
              ``(e["mount"]:""==Partitions::BootMount() && 
	         e["auto_added"]:false)))>0 )
	{
	foreach( map e, solution["solution","gap"]:[],
	    ``{
	    foreach( list a, e["added"]:[],
		``{
		integer pindex = a[0]:0;
		if( solution["partitions",pindex,"mount"]:"" == "/" &&
		    disk["cyl_count"]:0 > Partitions::BootCyl() &&
		    e["end"]:0 <= Partitions::BootCyl() && !need_boot(disk) )
		    {
		    remove_boot = true;
		    }
		});
	    });
	}
    integer index = 0;
    if( remove_boot )
	{
	foreach( map e, solution["solution","gap"]:[],
	    ``{
	    list nlist = [];
	    foreach( list a, e["added"]:[],
		``{
		integer pindex = a[0]:0;
		if( solution["partitions",pindex,"mount"]:"" == 
		    Partitions::BootMount() )
		    {
		    integer rest = a[2]:0;
		    y2milestone( "process_partition_data remove unneeded %3 %1 cyl %2", 
		                 e["added"]:[], rest, Partitions::BootMount() );
		    list<list> nlist = filter( list l, e["added"]:[], ``(l[0]:0!=pindex));
		    if( size(nlist)>0 && !e["exists"]:false )
			{
			list weight = maplist( list l, nlist, ``(l[2]:0) );
			map r = $[];
			r = distribute_space( rest, weight, nlist, 
			                      solution["partitions"]:[] );
			nlist = eval(r["added"]:[]);
			solution["solution","gap",index,"cylinders"] = 
			    e["cylinders"]:0 - r["diff"]:0;
			}
		    solution["solution","gap",index,"added"] = eval(nlist);
		    }
		pindex = pindex+1;
		});
	    index = index + 1;
	    });
	}
    if( solution["solution","resize_ext"]:0>0 )
	{
	disk["partitions"] = maplist( map p, disk["partitions"]:[],
		``{
		if( p["type"]:`unknown==`extended )
		    {
		    p["resize"] = true;
		    p["ignore_fs"] = true;
		    p["region",1] = solution["solution","resize_ext"]:0 -
				    p["region",0]:0 + 1;
		    p["size_k"] = p["region",1]:0 * disk["cyl_size"]:0 / 1024;
		    y2milestone( "process_partition_data resize ext %1", p );
		    }
		return( p );
		});
	}
    foreach( map e, solution["solution","gap"]:[],
	``{
	y2milestone( "process_partition_data e %1", e );
	if( e["exists"]:false )
	    {
	    integer index = 0;
	    integer pindex = e["added",0,0]:0;
	    string mount = solution["partitions",pindex,"mount"]:"";
	    integer fsid = Partitions::fsid_native;
	    if( mount == "swap" )
		{
		fsid = Partitions::fsid_swap;
		}
	    if( solution["partitions",pindex,"id"]:0 != 0 )
		{
		fsid = solution["partitions",pindex,"id"]:0;
		}
	    foreach( map p, disk["partitions"]:[],
		``{
		if( p["nr"]:0 == e["added",0,1]:0 )
		    {
		    y2milestone( "process_partition_data reuse part %1", p );
		    if( p["mount"]:"" != mount )
			p["inactive"] = true;
		    p = Storage::SetVolOptions( p, mount, 
					        solution["partitions",pindex,"fsys"]:`unknown,
						solution["partitions",pindex,"fopt"]:"",
						solution["partitions",pindex,"fstopt"]:"",
						solution["partitions",pindex,"label"]:"" );
		    if( p["fsid"]:0 != fsid )
			{
			p["change_fsid"] = true;
			p["ori_fsid"] = p["fsid"]:0;
			p["fsid"] = fsid;
			}
		    if( size(mount)==0 && size(vgname)>0 &&
		        p["type"]:`unknown!=`extended )
			p["vg"] = vgname;
		    disk["partitions",index] = p;
		    y2milestone( "process_partition_data reuse auto part %1", p );
		    }
		else if( (size(vgname)>0||e["resize"]:0>0) && 
		          p["nr"]:0 == e["nr"]:0 )
		    {
		    if( e["fsid"]:0!=0 && e["fsid"]:0!=p["fsid"]:0 )
			{
			p["change_fsid"] = true;
			p["ori_fsid"] = p["fsid"]:0;
			p["fsid"] = e["fsid"]:0;
			}
		    if( e["resize"]:0>0 )
			{
			p["resize"] = true;
			p["ignore_fs"] = true;
			p["region",1] = e["resize"]:0 - p["region",0]:0 + 1;
			p["size_k"] = p["region",1]:0 * 
			              disk["cyl_size"]:0 / 1024;
			}
		    if( size(vgname)>0 )
			p["vg"] = vgname;
		    disk["partitions",index] = p;
		    y2milestone( "process_partition_data resize part %1", p );
		    }
		index = index + 1;
		});
	    }
	else
	    {
	    list region = [ e["start"]:0, e["end"]:0-e["start"]:0+1 ];
	    map part = $[];
	    if( e["extended"]:false && e["created"]:0 > 0 )
		{
		while( e["added",0,1]:(disk["max_primary"]:4+1) <= 
		    disk["max_primary"]:4 )
		    {
		    integer pindex = e["added",0,0]:0;
		    string mount = solution["partitions",pindex,"mount"]:"";
		    integer fsid = Partitions::fsid_native;
		    if( mount == "swap" )
			{
			fsid = Partitions::fsid_swap;
			}
		    part["create"] = true;
		    part["nr"] = e["created"]:0;
		    part["device"] = Storage::GetDeviceName( dev, part["nr"]:-1 );
		    part["region"] = region;
		    part["region",1] = e["added",0,2]:0;
		    region[0] = region[0]:0 + part["region",1]:0;
		    region[1] = region[1]:0 - part["region",1]:0;
		    part["type"] = `primary;
		    part["inactive"] = true;
		    part = Storage::SetVolOptions( part, mount, 
						   solution["partitions",pindex,"fsys"]:`unknown,
						   solution["partitions",pindex,"fopt"]:"",
						   solution["partitions",pindex,"fstopt"]:"",
						   solution["partitions",pindex,"label"]:"" );
		    if( solution["partitions",pindex,"id"]:0 != 0 )
			{
			fsid = solution["partitions",pindex,"id"]:0;
			if( !haskey( solution["partitions",pindex]:$[], "fsys" ))
			    {
			    part["format"] = false;
			    }
			}
		    part["size_k"] = part["region",1]:0 * disk["cyl_size"]:0 / 1024;
		    part["fsid"] = fsid;
		    part["fstype"] = Partitions::FsIdToString( fsid );
		    if( size(mount)==0 && size(vgname)>0 )
			part["vg"] = vgname;
		    y2milestone( "process_partition_data auto partition %1", part );
		    partitions = add( partitions, part );
		    e["created"] = e["added",0,1]:0;
		    e["added"] = remove( e["added"]:[], 0 );
		    part = $[];
		    }
		part["create"] = true;
		part["nr"] = e["created"]:0;
		part["device"] = Storage::GetDeviceName( dev, part["nr"]:-1 );
		part["region"] = eval(region);
		part["type"] = `extended;
		part["fsid"] = Partitions::fsid_extended_win;
		part["fstype"] = Partitions::FsIdToString( part["fsid"]:0 );
		part["size_k"] = region[1]:0 * disk["cyl_size"]:0 / 1024;
		y2milestone( "process_partition_data extended auto partition %1", part );
		partitions = add( partitions, eval(part));
		}
	    foreach( list a, e["added"]:[],
		``{
		part = $[];
		integer pindex = a[0]:0;
		string mount = solution["partitions",pindex,"mount"]:"";
		integer fsid = Partitions::fsid_native;
		if( mount == "swap" )
		    {
		    fsid = Partitions::fsid_swap;
		    }
		part["create"] = true;
		part["nr"] = a[1]:0;
		part["device"] = Storage::GetDeviceName( dev, part["nr"]:0 );
		region[1] = a[2]:0;
		part["region"] = eval(region);
		region[0] = region[0]:0 + region[1]:0;
		part["size_k"] = region[1]:0 * disk["cyl_size"]:0 / 1024;
		part["type"] = `primary;
		if( e["extended"]:false )
		    {
		    part["type"] = `logical;
		    }
		part["inactive"] = true;
		part = Storage::SetVolOptions( part, mount, 
					       solution["partitions",pindex,"fsys"]:`unknown,
					       solution["partitions",pindex,"fopt"]:"",
					       solution["partitions",pindex,"fstopt"]:"",
					       solution["partitions",pindex,"label"]:"" );
		if( solution["partitions",pindex,"id"]:0 != 0 )
		    {
		    fsid = solution["partitions",pindex,"id"]:0;
		    if( !haskey( solution["partitions",pindex]:$[], "fsys" ))
			{
			part["format"] = false;
			}
		    y2milestone( "process_partition_data partition id %1 format %2 part %3", 
		                 fsid, part["format"]:false, 
				 solution["partitions",pindex]:$[] );
		    }
		part["fsid"] = fsid;
		part["fstype"] = Partitions::FsIdToString( fsid );
		if( size(mount)==0 && size(vgname)>0 )
		    part["vg"] = vgname;
		y2milestone( "process_partition_data auto partition %1", part );
		partitions = add( partitions, eval(part));
		});
	    partitions = sort( map a, map b, partitions, ``(a["nr"]:0<b["nr"]:0));
	    }
	});
    disk["partitions"] = union( disk["partitions"]:[], partitions );
    y2milestone( "process_partition_data disk %1", disk );
    return( disk );
    }


map add_cylinder_info( map conf, map gap )
    {
    integer cyl_size = gap["cyl_size"]:1;
    conf["partitions"] = 
	sort( map a, map b, conf["partitions"]:[],
	      ``({
	      if( a["primary"]:false != b["primary"]:false )
		  return( true );
	      else if( a["max_cyl"]:big_cyl != b["max_cyl"]:big_cyl )
		  return( a["max_cyl"]:big_cyl < b["max_cyl"]:big_cyl );
	      else
		  return( a["size"]:0 < b["size"]:0 );
	      }));
    y2milestone( "add_cylinder_info parts %1", conf["partitions"]:[] );
    integer sum = 0;
    conf["partitions"] = maplist( map p, conf["partitions"]:[],
	``{
	sum = sum + p["pct"]:0;
	p["cylinders"] = (p["size"]:0+cyl_size-1)/cyl_size;
	if( p["cylinders"]:0 == 0 )
	    {
	    p["cylinders"] = 1;
	    }
	return( p );
	});
    y2milestone( "add_cylinder_info sum %1", sum );
    y2milestone( "add_cylinder_info parts %1", conf["partitions"]:[] );
    if( sum>100 )
	{
	integer rest = sum - 100;
	conf["partitions"] = maplist( map p, conf["partitions"]:[],
	    ``{
	    if( haskey( p, "pct" ) )
		{
		integer pct = p["pct"]:0;
		integer diff = ((rest * pct) + sum/2) / sum;
		sum = sum - pct;
		rest = rest - diff;
		p["pct"] = pct - diff;
		}
	    return( p );
	    });
	}
    conf["partitions"] = maplist( map p, conf["partitions"]:[],
	``{
	if( haskey( p, "pct" ) )
	    {
	    integer cyl = gap["sum"]:0 / 100 * p["pct"]:0;
	    cyl = (cyl+cyl_size/2) / cyl_size;
	    if( cyl == 0 )
		{
		cyl = 1;
		}
	    p["want_cyl"] = cyl;
	    }
	if( p["maxsize"]:0 > 0 )
	    {
	    integer cyl = (p["maxsize"]:0+cyl_size-1) / cyl_size;
	    p["size_max_cyl"] = cyl;
	    if( p["want_cyl"]:0 > cyl )
		{
		p["want_cyl"] = cyl;
		}
	    }
	return( p );
	});
    y2milestone( "add_cylinder_info parts %1", conf["partitions"]:[] );
    return( conf );
    }


map get_perfect_list( list ps, map g )
    {
    y2milestone( "get_perfect_list ps %1", ps );
    y2milestone( "get_perfect_list gap %1", g );
    if( size(g["gap"]:[])>0 &&
        ((g["extended_possible"]:false && 
	    size(g["free_pnr"]:[])>0 &&
	    size(ps)+1 <= size(g["ext_pnr"]:[])+size(g["free_pnr"]:[])) ||
	 (!g["extended_possible"]:false &&
	    size(ps) <= size(g["ext_pnr"]:[])+size(g["free_pnr"]:[]))) )
	{
	map lg = (map) eval(g);
	lg["gap"] = maplist( map e, lg["gap"]:[], 
	    ``{ 
	    e["orig_cyl"] = e["cylinders"]:0;
	    e["added"] = [];
	    return( e );
	    });
	lg["procpart"] = 0;
	list lp = (list) eval(ps);
	if( g["extended_possible"]:false && size(ps)+1>size(g["free_pnr"]:[]))
	    {
	    y2milestone( "get_perfect_list creating extended" );
	    integer index = 0;
	    foreach( map e, lg["gap"]:[], 
		``{
		if( !e["exists"]:false )
		    {
		    map gap = (map) eval(lg);
		    gap["gap",index,"created"] = gap["free_pnr",0]:1;
		    gap["free_pnr"] = remove( gap["free_pnr"]:[1], 0 );
		    gap["gap",index,"extended"] = true;
		    add_part_recursive( ps, gap );
		    }
		index = index+1;
		});
	    }
	else
	    {
	    y2milestone( "get_perfect_list not creating extended" );
	    add_part_recursive( ps, lg );
	    }
	}
    map ret = $[];
    if( size(cur_gap)>0 )
	{
	ret["weight"] = cur_weight;
	ret["solution"] = eval(cur_gap);
	ret["partitions"] = eval(ps);
	}
    y2milestone( "get_perfect_list ret weight %1", ret["weight"]:-1000000 );
    y2milestone( "get_perfect_list ret solution %1", ret["solution","gap"]:[] );
    return( ret );
    }


void add_part_recursive( list ps, map g )
    {
    y2milestone( "add_part_recursive pindex %1", g["procpart"]:0 );
    y2milestone( "add_part_recursive ps %1", ps );
    y2milestone( "add_part_recursive gap %1", g );
    map lg = (map) eval(g);
    integer gindex = 0;
    integer pindex = lg["procpart"]:0;
    map part = ps[pindex]:$[];
    lg["procpart"] = pindex + 1;
    y2milestone( "add_part_recursive p %1", part );
    foreach( map e, lg["gap"]:[], 
	``{
	y2milestone( "add_part_recursive e %1", e );
	boolean max_cyl_ok = !haskey( part, "max_cyl" ) ||
	                     part["max_cyl"]:0 >= e["end"]:0;
	if( !max_cyl_ok )
	    {
	    integer cyl = 0;
	    foreach( list a, lg["gap",gindex,"added"]:[],
		``{
		cyl = cyl + ps[a[0]:0,"cylinders"]:0;
		});
	    cyl = cyl + part["cylinders"]:0;
	    y2milestone( "max_cyl_ok cyl %1", cyl );
	    max_cyl_ok = (e["start"]:0 + cyl) <= part["max_cyl"]:0;
	    }
	y2milestone( "add_part_recursive max_cyl_ok %1", max_cyl_ok );
	if( max_cyl_ok && part["cylinders"]:0 <= e["cylinders"]:0 &&
	    ((!e["extended"]:false && size(lg["free_pnr"]:[])>0) ||
	     (part["primary"]:false && e["created"]:0>0 && e["extended"]:false && size(lg["free_pnr"]:[])>0) || 
	     (!part["primary"]:false && e["extended"]:false && size(lg["ext_pnr"]:[])>0)))
	    {
	    map llg = (map) eval(lg);
	    if( e["exists"]:false )
		{
		llg["gap",gindex,"cylinders"] = 0;
		}
	    else
		{
		llg["gap",gindex,"cylinders"] = 
		    llg["gap",gindex,"cylinders"]:0 - part["cylinders"]:0;
		}
	    list addl = [ pindex ];
	    if( e["extended"]:false && !part["primary"]:false )
		{
		addl = add( addl, llg["ext_pnr",0]:5 );
		llg["ext_pnr"] = remove( llg["ext_pnr"]:[0], 0 );
		}
	    else if( !e["exists"]:false )
		{
		addl = add( addl, llg["free_pnr",0]:1 );
		llg["free_pnr"] = remove( llg["free_pnr"]:[0], 0 );
		}
	    else 
		{
		addl = add( addl, e["nr"]:0 );
		}
	    llg["gap",gindex,"added"] = 
		add( llg["gap",gindex,"added"]:[], addl );
	    if( pindex+1 < size(ps) )
		{
		add_part_recursive( ps, llg );
		}
	    else
		{
		map ng = normalize_gaps(ps, llg);
		integer val = do_weighting( ps, ng );
		y2milestone( "add_part_recursive val %1 cur_weight %2 size %3", 
		             val, cur_weight, size(cur_gap));
		if( val > cur_weight || size(cur_gap)==0 )
		    {
		    cur_weight = val;
		    cur_gap = (map)eval(ng);
		    }
		}
	    }
	gindex = gindex+1;
	});
    };


map normalize_gaps( list ps, map g )
    {
    y2milestone( "normalize_gaps gap %1", g );
    integer gindex = 0;
    integer pindex = 0;
    foreach( map e, g["gap"]:[],
	``{
	y2milestone( "normalize_gaps e %1", e );
	if( e["exists"]:false )
	    {
	    if( size(e["added"]:[])>0 && size(e["added",0]:[])==2 )
		{
		g["gap",gindex,"added",0] = 
		    add( e["added",0]:[], e["orig_cyl"]:1 );
		}
	    }
	else
	    {
	    integer rest = e["cylinders"]:0;
	    integer needed = 0;
	    integer tidx = 0;
	    foreach( list p, e["added"]:[],
		``{
		tidx = p[0]:0;
		if( ps[tidx,"want_cyl"]:0 > ps[tidx,"cylinders"]:0 )
		    {
		    needed = needed + ps[tidx,"want_cyl"]:0 -
		             ps[tidx,"cylinders"]:0;
		    }
		});
	    y2milestone( "normalize_gaps needed %1 rest %2", needed, rest );
	    if( needed > rest )
		{
                list tr = [];
		list weight = 
		    maplist( list l, e["added"]:[], 
			     ``({
			     integer idx = l[0]:0;
			     integer d = ps[idx,"want_cyl"]:0 - 
			                 ps[idx,"cylinders"]:0;
                             if( d>0 )
				 {
				 l = add( l, ps[idx,"cylinders"]:0 );
				 }
			     tr = add( tr, l );
			     return( d>0 ? d : 0 );
			     }));
	        y2milestone( "normalize_gaps tr %1", tr );
		map r = $[];
		r = distribute_space( rest, weight, tr, ps );
		g["gap",gindex,"added"] = eval(r["added"]:[]);
		g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
		y2milestone( "normalize_gaps partly satisfy %1 cyl %2", 
		             g["gap",gindex,"added"]:[],
			     g["gap",gindex,"cylinders"]:0 );
		}
	    else
		{
		g["gap",gindex,"cylinders"] = e["cylinders"]:0 - needed;
		}

	    pindex = 0;
	    foreach( list p, g["gap",gindex,"added"]:[], 
		``{
		if( size(p)<3 )
		    {
		    tidx = p[0]:0;
		    if( ps[tidx,"want_cyl"]:0 > ps[tidx,"cylinders"]:0 )
			{
			p = add( p, ps[tidx,"want_cyl"]:0 );
			}
		    else
			{
			p = add( p, ps[tidx,"cylinders"]:0 );
			}
		    g["gap",gindex,"added",pindex] = p;
		    y2milestone( "normalize_gaps satisfy p %1 cyl %2", p, 
		                 e["cylinders"]:0 );
		    }
		pindex = pindex+1;
		});
	    y2milestone( "normalize_gaps added %1", 
	                 g["gap",gindex,"added"]:[] );
	    }
	gindex = gindex + 1;
	});
    gindex = 0;
    foreach( map e, g["gap"]:[],
	``{
	if( !e["exists"]:false && e["cylinders"]:0>0 )
	    {
	    list<integer> weight = maplist( list l, e["added"]:[], 
	                           ``(ps[l[0]:0,"size"]:0==0 ? 1 : 0) );
	    if( find( integer l, weight, ``(l>0) ) != nil ) 
		{
		map r = $[];
		r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], 
		                      ps );
		g["gap",gindex,"added"] = eval(r["added"]:[]);
		g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
		y2milestone( "normalize_gaps increase max p %1 cyl %2", 
		             g["gap",gindex,"added"]:[], 
		             g["gap",gindex,"cylinders"]:0 );
		}
	    }
	gindex = gindex + 1;
	});
    gindex = 0;
    foreach( map e, g["gap"]:[],
	``{
	if( !e["exists"]:false && e["cylinders"]:0>0 && 
	    e["cylinders"]:0 < g["disk_cyl"]:0/20 )
	    {
	    list weight = maplist( list l, e["added"]:[], ``(l[2]:0) );
	    map r = $[];
	    r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], ps );
	    g["gap",gindex,"added"] = eval(r["added"]:[]);
	    g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
	    y2milestone( "normalize_gaps close small gap p %1 cyl %2", 
	                 g["gap",gindex,"added"]:[], 
	                 g["gap",gindex,"cylinders"]:0 );
	    }
	gindex = gindex + 1;
	});
    gindex = 0;
    foreach( map e, g["gap"]:[],
	``{
	if( !e["exists"]:false && e["cylinders"]:0>0 )
	    {
	    list<integer> weight = [];
	    weight =
		maplist( list l, e["added"]:[], 
			 ``((ps[l[0]:0,"increasable"]:false) ? 1 : 0));
	    y2milestone( "normalize_gaps w %1", weight );
	    if( find( integer l, weight, ``(l>0) ) != nil ) 
		{
		map r = $[];
		r = distribute_space( e["cylinders"]:0, weight, e["added"]:[], 
				      ps );
		g["gap",gindex,"added"] = eval(r["added"]:[]);
		g["gap",gindex,"cylinders"] = e["cylinders"]:0 - r["diff"]:0;
		e["cylinders"] = g["gap",gindex,"cylinders"]:0;
		y2milestone( "normalize_gaps increase increasable p %1 cyl %2", 
			     g["gap",gindex,"added"]:[],
			     g["gap",gindex,"cylinders"]:0 );
		}
	    }
        gindex = gindex + 1;
	});
    gindex = 0;
    foreach( map e, g["gap"]:[],
	``{
	if( !e["exists"]:false && e["extended"]:false && e["created"]:0 > 0 &&
	    size(e["added"]:[])==1 && e["cylinders"]:0==0 )
	    {
	    g["gap",gindex,"extended"] = false;
	    g["gap",gindex,"added",0,1] = e["created"]:0;
	    y2milestone( "normalize_gaps changed extended %1", 
	                 g["gap",gindex]:$[] );
	    }
	gindex = gindex + 1;
	});
    gindex = 0;
    map sort_map = $[ "/boot" : 0, "/boot/efi" : 0, "swap" : 1, 
                      "/" : 5, "/home" :6 ];
    foreach( map e, g["gap"]:[],
	``{
	if( !e["exists"]:false && size(e["added"]:[])>1 )
	    {
	    y2milestone( "normalize_gaps old  added %1", e["added"]:[] );
	    list nums = maplist( list l, e["added"]:[], ``(l[1]:-1));
	    y2milestone( "normalize_gaps old nums %1", nums );
	    list sdd = sort( list a, list b, e["added"]:[],
			     ``({
			     integer ai = a[0]:0;
			     integer bi = b[0]:0;
			     if( ps[ai,"primary"]:false != ps[bi,"primary"]:false )
				 return( ps[ai,"primary"]:false );
			     else if( ps[ai,"max_cyl"]:big_cyl != ps[bi,"max_cyl"]:big_cyl )
				 return( ps[ai,"max_cyl"]:big_cyl < ps[bi,"max_cyl"]:big_cyl );
			     else
				 return( sort_map[ps[ai,"mount"]:""]:3 < sort_map[ps[bi,"mount"]:""]:3);
			     }));
	    integer idx = 0;
	    foreach( list e, (list<list>)sdd, 
		``{
		sdd[idx,1] = nums[idx]:0;
		idx = idx+1;
		});
	    g["gap",gindex,"added"] = sdd;
	    y2milestone( "normalize_gaps sort added %1", 
	                 g["gap",gindex,"added"]:[] );
	    }
	gindex = gindex + 1;
	});
    y2milestone( "normalize_gaps gap %1", g );
    return( g );
    };


map distribute_space( integer rest, list weights, list added, list ps )
    {
    integer diff_sum = 0;
    integer sum = 0;
    integer index = 0;
    integer pindex = 0;
    integer scount = 0;
    y2milestone( "distribute_space rest %1 weights %2 added %3", rest, 
                 weights, added );
    integer loopcount=0;
    do
	{
	loopcount = loopcount+1;
	index = 0;
	sum = 0;
	scount = 0;
	foreach( list p, (list<list>)added,
	    ``{
	    pindex = p[0]:0;
	    if( ps[pindex,"size_max_cyl"]:0==0 || 
	        ps[pindex,"size_max_cyl"]:0 > p[2]:0)
		{
		sum = sum + weights[index]:0;
		y2milestone( "sum %1 weight %2 pindex %3", sum, 
		             weights[index]:0, pindex );
		scount = scount+1;
		}
	    index = index+1;
	    });
	index = 0;
	y2milestone( "distribute_space sum %1 rest %2 scount %3 added %4 lc %5",
	             sum, rest, scount, added, loopcount );
	foreach( list p, (list<list>)added,
	    ``{
	    pindex = p[0]:0;
	    if( size(p)==3 && sum>0 &&
		(ps[pindex,"size_max_cyl"]:0==0 ||
		 ps[pindex,"size_max_cyl"]:0 > p[2]:0) )
		{
		integer diff = ((rest*weights[index]:0) + sum/2) / sum;
		if( ps[pindex,"size_max_cyl"]:0>0 && 
		    diff > ps[pindex,"size_max_cyl"]:0-p[2]:0 )
		    {
		    diff = ps[pindex,"size_max_cyl"]:0-p[2]:0;
		    }
		sum = sum - weights[index]:0;
		rest = rest - diff;
		added[index,2] = added[index,2]:0 + diff;
		diff_sum = diff_sum + diff;
		y2milestone( "distribute_space sum %1 rest %2 diff %3 added %4", 
			     sum, rest, diff, added[index]:[] );
		}
	    index = index+1;
	    });
	}
    while( rest>0 && scount>0 && loopcount<3 );
    map ret = $[ "added":added, "diff" : diff_sum ];
    y2milestone( "distribute_space ret %1", ret );
    return( ret );
    }


integer do_weighting( list ps, map g )
    {
    y2milestone( "do_weighting gap %1", g["gap"]:[] );
    integer ret = 0;
    integer index = 0;
    integer diff = 0;
    if( cur_mode == `reuse )
	{
	ret = ret - 100;
	}
    else if( cur_mode == `resize )
	{
	ret = ret - 1000;
	}
    else if( cur_mode == `desparate )
	{
	ret = ret - 1000000;
	}
    y2milestone( "do_weighting after mode ret %1", ret );
    foreach( map e, g["gap"]:[],
	``{
	y2milestone( "do_weighting e %1", e );
	if( !e["exists"]:false && e["cylinders"]:0 > 0 )
	    {
	    diff = -5;
	    if( e["cylinders"]:0 < g["disk_cyl"]:0/20 )
		{
		diff = diff - 10;
		}
	    ret = ret + diff;
	    y2milestone( "do_weighting after gaps diff %1 ret %2", diff, ret );
	    }
	foreach( list p, e["added"]:[], 
	    ``{
	    index = p[0]:0;
	    if( e["exists"]:false && ps[index,"mount"]:""=="swap" && 
	        e["swap"]:false )
		{
		diff = 100;
		ret = ret + diff;
		y2milestone( "do_weighting after swap reuse diff %1 ret %2", 
		             diff, ret );
		}
	    if( ps[index,"want_cyl"]:0>0 )
		{
		diff = ps[index,"want_cyl"]:0 - p[2]:0;
		integer normdiff = diff * 100 / p[2]:0;
		if( diff < 0 )
		    {
		    normdiff = -normdiff;
		    }
		else if( diff > 0 )
		    {
		    normdiff = normdiff / 10;
		    }
		diff = ps[index,"want_cyl"]:0*g["cyl_size"]:1 / (100*1024*1024) -
		       normdiff;
		ret = ret + diff;
		y2milestone( "do_weighting after pct parts diff %1 ret %2", 
		             diff, ret );
		}
	    if( ps[index,"size"]:0==0 )
		{
		diff = p[2]:0 * g["cyl_size"]:1 / (50 * 1024 * 1024);
		ret = ret + diff;
		y2milestone( "do_weighting after maximizes parts diff %1 ret %2", 
		             diff, ret );
		}
	    if( ps[index,"size_max_cyl"]:0 > 0 && 
		ps[index,"size_max_cyl"]:0 < p[2]:0 )
		{
		diff = p[2]:0 - ps[index,"size_max_cyl"]:0;
		integer normdiff = diff * 100 / ps[index,"size_max_cyl"]:0;
		ret = ret - normdiff;
		y2milestone( "do_weighting after maximal size diff %1 ret %2", 
		             -normdiff, ret );
		}
	    });
	if( size(e["added"]:[])>0 && e["cylinders"]:0 > 0 )
	    {
	    integer diff = (e["cylinders"]:0 * g["cyl_size"]:1) / (1024*1024*1024);
	    ret = ret - diff;
	    y2milestone( "do_weighting after gap size diff %1 ret %2", -diff, ret );
	    }
	if( e["extended"]:false )
	    ret = ret-1;
	y2milestone( "do_weighting %1", ret );
	});
    y2milestone( "do_weighting ret %1", ret );
    return( ret );
    }


global list<map> try_remove_sole_extended( list<map> parts )
    {
    list<map> ret = parts;
    if( find( map p, ret,
	      ``(p["type"]:`unknown==`extended &&
		 !p["delete"]:false))!=nil &&
	find( map p, ret,
	      ``(p["type"]:`unknown==`logical &&
		 !p["delete"]:false))==nil )
	{
	ret = maplist( map p, ret,
	    ``{
	    if( p["type"]:`unknown == `extended )
		p["delete"] = true;
	    return( p );
	    });
	y2milestone( "try_remove_sole_extended delete extended p:%1", ret );
	}
    return( ret );
    }


map remove_possible_partitions( map disk, map conf )
    {
    map ret = (map)eval(disk);
    ret["partitions"] = maplist( map p, ret["partitions"]:[],
	``{
	integer fsid = p["fsid"]:0;
	if( (conf["remove_special_partitions"]:false ||
		!contains( Partitions::do_not_delete, fsid ) ) &&
	    p["type"]:`primary != `extended &&
	    !contains( conf["keep_partition_num"]:[], p["nr"]:0 ) &&
	    !contains( conf["keep_partition_id"]:[], fsid ) &&
	    !contains( conf["keep_partition_fsys"]:[], p["used_fs"]:`none ) &&
	    Storage::CanDelete( p, disk, false ))
	    {
	    p["delete"] = true;
	    }
	return( p );
	});
    ret["partitions"] = try_remove_sole_extended( ret["partitions"]:[] );
    return( ret );
    }


map try_resize_windows( map disk )
    {
    integer cyl_size = disk["cyl_size"]:1;
    map win = $[];
    map ret = (map)eval(disk);

    ret["partitions"] = maplist( map p, ret["partitions"]:[],
	``{
	integer fsid = p["fsid"]:0;
	if( Partitions::IsDosWinNtPartition( fsid ) ) 
	    {
	    win = p["winfo"]:$[];
	    y2milestone( "try_resize_windows win=%1", win );
	    if( win != nil && win["ok"]:false && p["size_k"]:0 > 1024*1024 &&
	        !win["efi"]:false )
		{
		p["winfo"] = win;
		p["resize"] = true;
		p["region",1] = (win["new_size"]:0 + cyl_size - 1) / cyl_size;
		p["win_max_length"] = 
		    (win["max_win_size"]:0 + cyl_size - 1) / cyl_size;
		y2milestone( "try_resize_windows win part %1", p );
		}
	    }
	return( p );
	});
    return( ret );
    }


list<map> get_gaps( integer start, integer end, list<map> part, 
			   boolean add_exist_linux )
    {
    y2milestone( "get_gaps start %1 end %2 add_exist %3", start, end, 
                 add_exist_linux );
    list<map> ret = [];
    map entry = $[];
    foreach( map p, part, 
	``{
	integer s = p["region",0]:0;
	integer e = s + p["region",1]:1 - 1;
	entry = $[];
	if( start < s )
	    {
	    entry["start"] = start;
	    entry["end"] = s-1;
	    ret = add( ret, eval(entry) );
	    }
	if( add_exist_linux && size(p["mount"]:"")==0 &&
	    (p["fsid"]:0==Partitions::fsid_native || 
	     p["fsid"]:0==Partitions::fsid_swap) )
	    {
	    entry["swap"] = p["fsid"]:0==Partitions::fsid_swap;
	    entry["start"] = s;
	    entry["end"] = e;
	    entry["exists"] = true;
	    entry["nr"] = p["nr"]:0;
	    ret = add( ret, entry );
	    }
	start = e+1;
	});
    if( start < end )
	{
	entry = $[];
	entry["start"] = start;
	entry["end"] = end;
	ret = add( ret, entry );
	}
    y2milestone( "get_gaps ret %1", ret );
    return( ret );
    }


map get_gap_info( map disk, boolean add_exist_linux )
    {
    map ret = $[];
    list<map> gap = [];
    list<map> plist = filter( map p, disk["partitions"]:[], 
                              ``(!p["delete"]:false) );
    plist = sort( map a, map b, plist, ``(a["region",0]:0<b["region",0]:0) );
    list<integer> exist_pnr = sort( maplist( map e, plist, ``(e["nr"]:0) ));
    if( disk["label"]:""=="mac" && !contains( exist_pnr, 1 ) )
	{
	exist_pnr = add( exist_pnr, 1 );
	}
    integer max_prim = Partitions::MaxPrimary(disk["label"]:"msdos");
    boolean has_ext = Partitions::HasExtended( disk["label"]:"msdos" );
    integer max_pos_cyl = Storage::MaxCylLabel( disk, 0 );
    integer end = 0;
    if( has_ext )
	{
	map ext = filter( map p, plist,
			  ``(p["type"]:`primary == `extended))[0]:$[];
	ret["extended_possible"] = size(ext)==0;
	ret["ext_reg"] = ext["region"]:[];
	if( size(ext)>0 )
	    {
	    end = ext["region",0]:0 + ext["region",1]:1-1;
	    if( end > ext["region",0]:0 + max_pos_cyl )
		end = ext["region",0]:0 + max_pos_cyl;
	    gap = get_gaps( ext["region",0]:0, end,
			    filter( map p, plist, ``(p["nr"]:0>max_prim)),
			    add_exist_linux );
	    gap = maplist( map e, gap, 
		``{
		e["extended"]=true;
		return e;
		});
	    plist = filter( map p, plist, ``(p["nr"]:0<=max_prim));
	    }
	}
    else
	{
	ret["extended_possible"] = false;
	}
    end = disk["cyl_count"]:1-1;
    if( end > max_pos_cyl )
	end = max_pos_cyl;
    integer start = 0;
    if( disk["label"]:"" == "sun" )
	start = 1;
    gap = (list<map>)union( gap, get_gaps( start, end, plist, add_exist_linux ));
    integer av_size = 0;
    gap = maplist( map e, gap,
	``{
	e["cylinders"] = e["end"]:0 - e["start"]:0 + 1;
	e["size"] = e["cylinders"]:0 * disk["cyl_size"]:1;
	av_size = av_size + e["size"]:0;
	return( e );
	});
    gap = maplist( map e, gap,
	``{
	e["sizepct"] = (e["size"]:0 * 201 / 2) / av_size;
	if( e["sizepct"]:0 == 0 )
	    {
	    e["sizepct"] = 1;
	    }
	return( e );
	});
    ret["cyl_size"] = disk["cyl_size"]:1;
    ret["disk_cyl"] = disk["cyl_count"]:1;
    ret["sum"] = av_size;
    integer max_pnr = max_prim;
    integer pnr = 1;
    list free_pnr = [];
    y2milestone( "get_gap_info exist_pnr %1", exist_pnr );
    while( pnr<=max_pnr )
	{
	if( !contains( exist_pnr, pnr ) )
	    {
	    free_pnr = add( free_pnr, pnr );
	    }
	pnr = pnr + 1;
	}
    ret["free_pnr"] = free_pnr;
    list<integer> ext_pnr = [ 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
		    25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,
		    45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63];
    integer max_logical = disk["max_logical"]:15;
    if( max_logical<63 )
	{
	ext_pnr = filter( integer i, ext_pnr, ``(i<=max_logical));
	}
    if( !has_ext )
	{
	ext_pnr = [];
	}
    else
	{
	integer maxlog = size(filter( integer i, exist_pnr, ``(i>max_pnr))) + 4;
	ext_pnr = filter( integer i, ext_pnr, ``(i>maxlog));
	}
    ret["ext_pnr"] = ext_pnr;
    ret["gap"] = gap;
    y2milestone( "get_gap_info ret %1", ret );
    return( ret );
    }


/**
 * Read partition data from XML control file
 * @return map flexible proposal map
 */
map read_partition_xml_config()
    {
    map xmlflex = (map)ProductFeatures::GetFeature ("partitioning", "flexible_partitioning");
    y2milestone("xml input: %1", xmlflex );

    map conf = $[];
    conf["prefer_remove"] = xmlflex["prefer_remove"]:true;
    conf["remove_special_partitions"] =
	xmlflex["remove_special_partitions"]:false;
    conf["keep_partition_id"] = [];
    conf["keep_partition_num"] = [];
    conf["keep_partition_fsys"] = [];

    foreach( string key, [ "keep_partition_id", "keep_partition_num"], 
        ``{
	list num = [];
	list<string> nlist = splitstring(xmlflex[key]:"", ",");
	foreach( string n, nlist, ``{ num = union( num, [tointeger(n)] );});
	conf[key] = num;
	});

    list fsys = [];
    list<string> nlist = splitstring( xmlflex["keep_partition_fsys"]:"" , "," );
    foreach( string n, nlist,
	     ``{
	     symbol fs = FileSystems::FsToSymbol(n);
	     if( fs != `none )
		 {
		 fsys = union( fsys, [ fs ] );
		 }
	     });
    conf["keep_partition_fsys"] = fsys;
    list partitions = [];
    foreach(map p, xmlflex["partitions"]:[],
	    ``{
	    map partition = $[];

	    if (p["disk"]:0 != 0)
		{
		partition["disk"] = p["disk"]:0;
		}
	    if (p["id"]:0 != 0)
		{
		partition["id"] = p["id"]:0;
		}

	    if (p["fstopt"]:"" != "")
		{
		partition["fstopt"] = p["fstopt"]:"";
		}

	    if (p["formatopt"]:"" != "")
		{
		partition["fopt"] = p["formatopt"]:"";
		}

	    partition["increasable"] = p["increasable"]:false;

	    if (p["mount"]:"" != "")
		{
		partition["mount"] = p["mount"]:"";
		}

	    if (p["percent"]:-1 != -1)
		{
		partition["pct"] = p["percent"]:100;
		}

	    if (p["label"]:"" != "")
		{
		partition["label"] = p["label"]:"";
		}

	    if (p["maxsize"]:"" != "")
		{
		partition["maxsize"] = Storage::ClassicStringToByte(p["maxsize"]:"");
		}

	    if (p["fsys"]:"" != "")
		{
		symbol fs = FileSystems::FsToSymbol(p["fsys"]:"");
		if( fs != `none )
		    {
		    partition["fsys"] = fs;
		    }
		}

	    if (p["size"]:"" != "")
		{
		string s = p["size"]:"";
		if( tolower(s) == "auto" )
		    {
		    partition["size"] = -1;
		    }
		else if( tolower(s) == "max" )
		    {
		    partition["size"] = 0;
		    }
		else
		    {
		    partition["size"] = Storage::ClassicStringToByte(s);
		    }
		}

	    if( partition["size"]:0 == -1 && partition["mount"]:"" == "swap" )
		{
		partition["size"] = 1024*1024*Partitions::SwapSizeMb(0);
		}
	    if( partition["mount"]:"" == Partitions::BootMount() )
		{
		if( partition["size"]:0 == -1 )
		    {
		    partition["size"] = Partitions::MinimalNeededBootsize();
		    }
		if( partition["fsys"]:`none == `none )
		    {
		    partition["fsys"] = Partitions::DefaultBootFs();
		    }
		if( partition["id"]:0 == 0 )
		    {
		    partition["id"] = Partitions::FsidBoot();
		    }
		partition["max_cyl"] = Partitions::BootCyl();
		}
	    if( partition["size"]:0 == -1 )
		{
		partition["size"] = 0;
		}

	    y2milestone("partition: %1", partition);
	    if( size(partition["mount"]:"")>0 || partition["id"]:0 > 0 )
		{
		partitions = add( partitions, partition );
		}
	    });
    conf["partitions"] = partitions;
    if( size(partitions)==0 )
	{
	conf = $[];
	}
    else
	{
	conf["partitions"] = partitions;
	}
    y2milestone( "conf %1", conf );
    return( conf );
    }


map read_partition_config( string fpath )
    {
    integer pos = 0;
    string line = "";
    string rex = "";
    map conf = $[];

    conf["prefer_remove"] = true;
    conf["remove_special_partitions"] = false;
    conf["keep_partition_id"] = [];
    conf["keep_partition_num"] = [];
    conf["keep_partition_fsys"] = [];

    string cstring = (string) SCR::Read( .target.string, fpath );
    list<string> lines = filter( string e, splitstring( cstring, "\n" ), ``(size(e)>0) );
    rex = "[ \t]*#.*";
    y2milestone( "lines %1", lines );
    lines = filter( string e, lines, ``(!regexpmatch( e, "[ \t]*#.*" )));
    y2milestone( "lines %1", lines );
    list fnd = [];
    foreach( string key, [ "PREFER_REMOVE", "REMOVE_SPECIAL_PARTITIONS" ],
	``{
	rex = "[ \t]*" + key + "[ \t]*=";
	fnd = filter( string e, lines, ``(regexpmatch( e, rex )));
	y2milestone( "rex %1 fnd %2", rex, fnd );
	if( size(fnd)>0 )
	    {
	    line = deletechars( fnd[size(fnd)-1]:"", "\t " );
	    pos = findlastof( line, "=" );
	    if( pos > 0 )
		{
		conf[tolower(key)] = tointeger( substring( line, pos+1 ))>0;
		}
	    }
	});
    foreach( string key, [ "KEEP_PARTITION_ID", "KEEP_PARTITION_NUM" ],
	``{
	rex = "[ \t]*" + key + "[ \t]*=";
	y2milestone( "rex %1", rex );
	foreach( string l, filter( string e, lines, ``(regexpmatch( e, rex ))),
	    ``{
	    y2milestone( "line %1", l );
	    line = deletechars( l, "\t " );
	    pos = findlastof( line, "=" );
	    if( pos > 0 )
		{
		list num = [];
		list<string> nlist = splitstring( substring( line, pos+1 ), "," );
		foreach( string n, nlist, ``{ num = union( num, [tointeger(n)] );});
		conf[tolower(key)] = union( conf[tolower(key)]:[], num );
		}
	    });
	});
    list fsys = [];
    rex = "[ \t]*" + "KEEP_PARTITION_FSYS" + "[ \t]*=";
    foreach( string l, filter( string e, lines, ``(regexpmatch( e, rex ))),
	``{
	y2milestone( "line %1", l );
	line = deletechars( l, "\t " );
	pos = findlastof( line, "=" );
	if( pos > 0 )
	    {
	    list<string> nlist = splitstring( substring( line, pos+1 ), "," );
	    foreach( string n, nlist, 
		``{ 
		symbol fs = FileSystems::FsToSymbol(n);
		if( fs != `none )
		    {
		    fsys = union( fsys, [ fs ] );
		    }
		});
	    }
	});
    conf["keep_partition_fsys"] = fsys;
    list partitions = [];
    map part = $[];
    rex = "[ \t]*" + "PARTITION" + "[ \t][ \t]*";
    foreach( string l, filter( string e, lines, ``(regexpmatch( e, rex ))),
	``{
	y2milestone( "line %1", l );
	string par = "";
	string key = "";
	integer pos = search( l, "PARTITION" );
	line = substring( l, pos+10 );
	y2milestone( "line %1", line );
	pos = search( line, "=" );
	part = $[];
	while( pos!=nil )
	    {
	    key = deletechars( substring( line, 0, pos ), " \t" );
	    line = substring( line, pos+1 );
	    if( substring( line, 0, 1 ) == "\"" )
		{
		line = substring( line, 1 );
		pos = search( line, "\"" );
		par = substring( line, 0, pos );
		line = substring( line, pos+1 );
		}
	    else
		{
		pos = findfirstof( line, " \t" );
		if( pos==nil )
		    {
		    par = line;
		    }
		else
		    {
		    par = substring( line, 0, pos );
		    line = substring( line, pos+1 );
		    }
		}
	    y2debug( "key %1 par \"%2\"", key, par );
	    if( key == "id" )
		{
		part[key] = tointeger(par);
		}
	    else if( key == "mount" )
		{
		part[key] = par;
		}
	    else if( key == "increasable" )
		{
		part["increasable"] = tointeger(par)>0 ? true : false;
		}
	    else if( key == "size" )
		{
		if( tolower(par) == "auto" )
		    {
		    part["size"] = -1;
		    }
		else if( tolower(par) == "max" )
		    {
		    part["size"] = 0;
		    }
		else
		    {
		    part["size"] = Storage::ClassicStringToByte(par);
		    }
		}
	    else if( key == "label" )
		{
		part[key] = par;
		}
	    else if( key == "maxsize" )
		{
		part[key] = Storage::ClassicStringToByte(par);
		}
	    else if( key == "sizepct" )
		{
		part["pct"] = tointeger(par);
		}
	    else if( key == "disk" )
		{
		part[key] = tointeger(par);
		}
	    else if( key == "fsys" )
		{
		symbol fs = FileSystems::FsToSymbol(par);
		if( fs != `none )
		    {
		    part[key] = fs;
		    }
		}
	    else if( key == "fstopt" )
		{
		part[key] = par;
		}
	    else if( key == "formatopt" )
		{
		part["fopt"] = par;
		}
	    pos = search( line, "=" );
	    }
	y2milestone( "part %1", part );
	if( part["size"]:0 == -1 && part["mount"]:"" == "swap" )
	    {
	    part["size"] = 1024*1024*Partitions::SwapSizeMb(0);
	    }
	if( part["mount"]:"" == Partitions::BootMount() )
	    {
	    if( part["size"]:0 == -1 )
		{
		part["size"] = Partitions::MinimalNeededBootsize();
		}
	    if( part["fsys"]:`none == `none )
		{
		part["fsys"] = Partitions::DefaultBootFs();
		}
	    if( part["id"]:0 == 0 )
		{
		part["id"] = Partitions::FsidBoot();
		}
	    part["max_cyl"] = Partitions::BootCyl();
	    }
	if( part["size"]:0 == -1 )
	    {
	    part["size"] = 0;
	    }
	if( size(part["mount"]:"")>0 || part["id"]:0 > 0 )
	    {
	    partitions = add( partitions, part );
	    }
	});
    conf["partitions"] = partitions;
    if( size(partitions)==0 )
	{
	conf = $[];
	}
    else
	{
	conf["partitions"] = partitions;
	}
    y2milestone( "conf %1", conf );
    return( conf );
    }


global map can_swap_reuse( string disk, list<map> partitions, map<string,map> tgmap )
    {
    map ret = $[];
    y2milestone( "can_swap_reuse disk %1 partitions %2", disk, partitions );
    list<map> swaps = filter( map p, partitions,
			      ``(p["type"]:`unknown!=`free &&
				 !p["delete"]:false &&
				 p["detected_fs"]:`unknown==`swap));
    swaps = filter( map p, swaps, ``(check_swapable(p["device"]:"")));
    swaps = sort( map a, map b, swaps, ``(a["size_k"]:0>b["size_k"]:0));
    y2milestone( "can_swap_reuse swaps %1", swaps );
    if( swaps[0,"size_k"]:0 >= 128*1024 )
	{
	ret["partitions"] = 
	    maplist( map p, partitions,
		     ``{
		     if( !p["delete"]:false &&
			 p["device"]:""==swaps[0,"device"]:"" )
			 {
			 if( p["mount"]:"" != "swap" )
			     p["inactive"] = true;
			 p["mount"] = "swap";
			 if( haskey( p, "vg" ))
			     {
			     p = remove( p, "vg" );
			     if( p["change_fsid"]:false )
				 p["fsid"] = p["ori_fsid"]:Partitions::fsid_swap;
			     }
			 }
		     return( p );
		     });
	}
    else 
	{
	swaps = [];
	map<string,map> tg = filter( string k, map d, tgmap,
				     ``(Storage::IsPartitionable(d)));
	if( haskey( tg, disk ))
	    tg = remove( tg, disk );
	y2milestone( "can_swap_reuse tg wo %1", tg );
	foreach( string dev, map disk, tg,
	    ``{
	    list<map> sw = filter( map p, disk["partitions"]:[],
			      ``(p["type"]:`unknown!=`extended &&
				 !p["delete"]:false &&
				 p["detected_fs"]:`unknown==`swap));
	    sw = filter( map p, sw, ``(check_swapable(p["device"]:"")));
	    y2milestone( "can_swap_reuse disk %1 sw %2", dev, sw );
	    swaps = (list<map>)union( swaps, sw );
	    });
	swaps = sort( map a, map b, swaps, ``(a["size_k"]:0>b["size_k"]:0));
	y2milestone( "can_swap_reuse swaps %1", swaps );
	if( swaps[0,"size_k"]:0 >= 256*1024 )
	    {
	    ret["targets"] = Storage::SetPartitionData( tgmap, 
	                                                swaps[0,"device"]:"",
						        "mount", "swap" );
	    ret["targets"] = Storage::DelPartitionData( ret["targets"]:$[], 
	                                                swaps[0,"device"]:"",
						        "vg" );
	    }
	}
    y2milestone( "can_swap_reuse ret %1", ret );
    return( ret );
    }


list<map> can_boot_reuse( string disk, string label, boolean boot,
                          integer max_prim, list<map> partitions )
    {
    list<map> ret = [];
    y2milestone( "can_boot_reuse boot:%1", boot );
    if( boot && !Partitions::PrepBoot() )
	{
	y2milestone( "can_boot_reuse disk:%1 max_prim:%2 label:%3 part:%4", 
	             disk, max_prim, label, partitions );
	list<map> pl = [];
	pl = filter( map p, partitions, 
		     ``(!p["delete"]:false &&
			p["size_k"]:0*1024 >= 
			    Partitions::MinimalRequiredBootsize()));
	map boot = find( map p, pl,
			 ``((p["fsid"]:0 == Partitions::fsid_gpt_boot) ||
			    (p["fsid"]:0 == Partitions::FsidBoot() && 
			     p["size_k"]:0<500*1024)||
			    (p["detected_fs"]:`unknown==`hfs && 
			     p["boot"]:false && label=="mac") ||
			    (p["fsid"]:0 == Partitions::fsid_prep_chrp_boot &&
			     p["nr"]:0 <= max_prim &&
			     Partitions::PrepBoot())));
	if( boot != nil )
	    {
	    ret = maplist( map p, partitions,
			   ``{
			   if( !p["delete"]:false && 
			       p["device"]:""==boot["device"]:"" &&
			       Storage::CanEdit(p,false) )
			       {
			       p = Storage::SetVolOptions( p, Partitions::BootMount(),
			                                   Partitions::DefaultBootFs(), 
							   "", "", "" );
			       }
			   return( p );
			   });
	    }
	y2milestone( "can_boot_reuse ret:%1", ret );
	}
    return( ret );
    }


list<map> can_mp_reuse( string mp, integer min, integer max,
                        list<map> partitions )
    {
    list<map> ret = [];
    if( max>0 )
	max = max + max/10;
    y2milestone( "can_mp_reuse mp %1 min %2 max %3", mp, min, max );
    list<map> pl = [];
    pl = filter( map p, partitions, 
		 ``(!p["delete"]:false &&
		    p["fsid"]:Partitions::fsid_native == 
		        Partitions::fsid_native &&
		    p["used_by_type"]:`UB_NONE == `UB_NONE &&
		    size(p["mount"]:"")==0 &&
		    p["size_k"]:0/1024 >= min &&
		    (max==0 || p["size_k"]:0/1024 <= max) &&
		    Storage::CanEdit( p, false )));
    y2milestone( "can_mp_reuse normal %1", pl );
    if( size(pl)>0 )
	{
	pl = sort( map a, map b, pl, ``(a["size_k"]:0>b["size_k"]:0));
	y2milestone( "can_mp_reuse sorted %1", pl );
	ret = maplist( map p, partitions,
		       ``{
		       if( !p["delete"]:false &&
			   p["device"]:""==pl[0,"device"]:"" )
			   {
			   p = Storage::SetVolOptions( p, mp, 
			                               Partitions::DefaultFs(),
						       "", "", "" );
			   }
		       return( p );
		       });
	}
    y2milestone( "can_mp_reuse ret %1", ret );
    return( ret );
    }


integer get_avail_size_mb( list<map> parts )
    {
    integer ret = 0;
    foreach( map pp, filter( map p, parts, ``(p["delete"]:false &&
					      p["type"]:`unknown!=`extended)),
	     ``{
	     ret = ret + pp["size_k"]:0/1024;
	     });
    y2milestone( "get_avail_size_mb ret %1", ret );
    return( ret );
    }


    list<integer> get_swap_sizes(integer space)
    {
	list<integer> l = [ Partitions::SwapSizeMb(0), Partitions::SwapSizeMb(space) ];
	y2milestone("get_swap_sizes space %1 ret %2", space, l);
	return l;
    }


global list<map> get_proposal( boolean have_swap, map disk )
    {
    list<map> ret = [];
    y2milestone( "get_proposal have_swap:%1 disk %2", have_swap, disk );
    map root = $[ "mount" : "/", "increasable" : true,
                  "fsys" : Partitions::DefaultFs(), "size" : 0 ];
    map opts = GetControlCfg();
    map conf = $[ "partitions" : [] ];
    list<integer> swap_sizes = [];
    integer avail_size = get_avail_size_mb(disk["partitions"]:[]);
    if( !have_swap )
	{
	swap_sizes = get_swap_sizes( avail_size );
	map swap = $[ "mount" : "swap", "increasable" : true, "fsys" : `swap,
		      "maxsize" : 2*1024*1024*1024,
		      "size"  : swap_sizes[0]:256*1024*1024 ];
	conf["partitions"] = add( conf["partitions"]:[], swap );
	}
    conf["partitions"] = add( conf["partitions"]:[], root );
    map old_root = $[];
    if( GetProposalHome() && opts["home_limit"]:0 < avail_size )
	{
	map home = $[ "mount" : "/home", "increasable" : true, 
	              "fsys" : Partitions::DefaultFs(), "size" : 512*1024*1024, 
		      "pct" : 100-opts["root_percent"]:40 ];
	conf["partitions"] = maplist( map p, conf["partitions"]:[],
				      ``{
				      if( p["mount"]:""=="/" )
					  {
					  old_root = p;
					  p["pct"] = opts["root_percent"]:40;
					  p["maxsize"] = opts["root_max"]:0*1024*1024;
					  p["size"] = opts["root_base"]:0*1024*1024;
					  }
				      return( p );
				      });
	conf["partitions"] = add( conf["partitions"]:[], home );
	}
    map ps1 = do_flexible_disk_conf( disk, conf, false, false );
    if( size(old_root)>0 && !ps1["ok"]:false )
	{
	conf["partitions"] = 
	    filter( map p, conf["partitions"]:[],
		    ``(p["mount"]:""!="/home" && p["mount"]:""!="/"));
	conf["partitions"] = add( conf["partitions"]:[], old_root );
	ps1 = do_flexible_disk_conf( disk, conf, false, false );
	}
    if( !have_swap )
	{
	integer diff = swap_sizes[0]:256 - swap_sizes[1]:256;
	if( diff<0 )
	    diff = -diff;
	y2milestone( "get_proposal diff:%1 ps1 ok:%2", diff, ps1["ok"]:false );
	if( (!ps1["ok"]:false && diff>0) || diff>100 )
	    {
	    conf["partitions",0,"size"] = swap_sizes[1]:256*1024*1024;
	    map ps2 = do_flexible_disk_conf( disk, conf, false, false );
	    y2milestone( "get_proposal ps2 ok:%1", ps2["ok"]:false );
	    if( ps2["ok"]:false )
		{
		map rp1 = find( map p, ps1["disk","partitions"]:[], 
				``(!p["delete"]:false && p["mount"]:""=="/"));
		map rp2 = find( map p, ps2["disk","partitions"]:[], 
				``(!p["delete"]:false && p["mount"]:""=="/"));
		y2milestone( "get_proposal rp1:%1", rp1 );
		y2milestone( "get_proposal rp2:%1", rp2 );
		if( rp1==nil || (rp2!=nil && rp2["size_k"]:0>rp1["size_k"]:0 ))
		    ps1 = ps2;
		}
	    }
	}
    if( ps1["ok"]:false )
	ret = ps1["disk","partitions"]:[];
    y2milestone("get_proposal ret:%1", ret);
    return ret;
    }


integer get_usable_size_mb( map disk, boolean reuse_linux )
    {
    integer ret = 0;
    integer cyl_size = disk["cyl_size"]:0;
    integer disk_cyl = disk["cyl_count"]:0;
    list<map> partitions = filter( map p, disk["partitions"]:[], 
				   ``(!p["create"]:false));
    partitions = sort( map p1, map p2, partitions,
                           ``(p1["region",0]:0 < p2["region",0]:0));
    integer last_end = 0;
    foreach( map p, partitions,
	``{
	if( p["region",0]:0>last_end )
	    ret = ret + (p["region",0]:0-last_end) * cyl_size;
	if( p["type"]:`unknown!=`extended && 
	    (p["delete"]:false || (reuse_linux && p["linux"]:false)))
	    ret = ret + p["size_k"]:0 * 1024;
	last_end = p["region",0]:0;
	if( p["type"]:`unknown != `extended )
	    last_end = last_end + p["region",1]:0;
	});
    if( last_end < disk_cyl )
	ret = ret + (disk_cyl-last_end) * cyl_size;
    ret = ret / (1024*1024);
    return( ret );
    }


integer get_mb_sol( map sol, string mp )
    {
    map pa = find( map p, sol["disk","partitions"]:[], ``(p["mount"]:""==mp));
    y2milestone( "get_mb_sol pa %1", pa );
    integer ret = pa!=nil ? pa["size_k"]:0/1024 : 0;
    y2milestone( "get_mb_sol ret %1", ret );
    return( ret );
    }


integer get_vm_sol( map sol )
    {
    integer ret = 0;
    foreach( map p, sol["disk","partitions"]:[],
	``{
	if( !p["delete"]:false && (size(p["mount"]:"")>0 || size(p["vg"]:"")>0))
	    ret = ret + p["size_k"]:0/1024;
	});
    y2milestone( "get_vm_sol ret %1", ret );
    return( ret );
    }


list<map> special_boot_proposal_prepare( list<map> partitions )
    {
    list<map> ret = partitions;
    if( Partitions::PrepBoot() )
	{
	ret = maplist( map p, partitions,
		       ``{
		       if( size(p["mount"]:"")==0 &&
		           (p["fsid"]:0 == 0x06 || p["fsid"]:0 == 0x41) )
			   p["delete"] = true;
		       return( p );
		       });
	y2milestone( "special_boot_proposal_prepare part:%1", partitions );
	y2milestone( "special_boot_proposal_prepare  ret:%1", ret );
	}
    return( ret );
    }


map<string,map> prepare_part_lists( list<string> ddev, map<string,map> tg )
    {
    list linux_pid = [ Partitions::fsid_native, Partitions::fsid_swap,
                       Partitions::fsid_lvm, Partitions::fsid_raid ];
    foreach( string s, ddev,
	``{
	tg[s,"partitions"] = 
	    maplist( map p, tg[s,"partitions"]:[],
		``{
		if( contains( linux_pid, p["fsid"]:0 ) ||
		    (p["fsid"]:0 == Partitions::fsid_gpt_boot) ||
		    (p["fsid"]:0 == Partitions::FsidBoot() && 
		     p["size_k"]:0<500*1024)||
		    (Partitions::PrepBoot() && 
		     (p["fsid"]:0 == Partitions::fsid_prep_chrp_boot||
		      p["fsid"]:0 == 0x06)))
		    p["linux"] = true;
		else
		    p["linux"] = false;
		return( p );
		});
	});
    return( tg );
    }


list<string> get_disk_try_list( map<string,map> tg, boolean soft )
    {
    list<string> ret = [];
    ret = maplist( string k, map e, 
		   filter( string l, map f, tg, 
			   ``(!ignore_disk(l,f,soft))), ``(k));
    ret = sort( ret );
    if( size(ret)>4 )
	{
	ret = restrict_disk_names( ret );
	}
    y2milestone( "get_disk_try_list soft:%1 ret:%2", soft, ret );
    return( ret );
    }


boolean usable_for_win_resize( map p, boolean assert_cons_fs )
    {
    boolean ret = Partitions::IsDosWinNtPartition( p["fsid"]:0 ) &&
		  p["size_k"]:0 > 1024*1024 && 
		  !p["resize"]:false && !p["delete"]:false;
    if( ret )
	{
	if( assert_cons_fs )
	    ret = p["winfo","ok"]:false && !p["winfo","efi"]:false;
	else
	    ret = size(p["winfo"]:$[])>0;
	}
    return( ret );
    }


list<map> remove_p_settings( list<map> parts, list<string> mp )
    {
    list<string> rems = [ "resize", "used_fs", "win_max_length", "mount",
                          "format", "ignore_fs", "vg" ];
    parts = 
	maplist( map p, parts,
	    ``{
	    if( (size(mp)==0 || contains( mp, p["mount"]:"")) &&
	        !(p["mount"]:""=="swap" && !p["inactive"]:false) )
		{
		foreach( string s, rems,
		    ``{
		    if( haskey( p, s ) )
			p = remove( p, s );
		    });
		}
	    return( p );
	    });
    return( parts );
    }


list<map> remove_one_partition( map disk )
    {
    list<map> partitions = disk["partitions"]:[];
    list<map> pl = filter( map p, partitions,
			   ``( p["linux"]:false && size(p["mount"]:"")==0 &&
			       !p["delete"]:false && 
			       Storage::CanDelete( p, disk, false )));
    if( size(pl)>0 )
	{
	pl = sort( map a, map b, pl, 
		   ``(a["size_k"]:0>b["size_k"]:0));
	pl = (list<map>) union( 
	    filter( map p, pl, ``(p["used_by_type"]:`UB_NONE==`UB_NONE) ),
	    filter( map p, pl, ``(p["used_by_type"]:`UB_NONE!=`UB_NONE) ));

	partitions = 
	    maplist( map p, partitions,
		``{
		if( p["linux"]:false && !p["delete"]:false &&
		    size(p["mount"]:"")==0 &&
		    p["device"]:""==pl[0,"device"]:"" &&
		    Storage::CanDelete( p, disk, false ))
		    {
		    p["delete"] = true;
		    y2milestone( "remove_one_partition p %1", p );
		    }
		return( p );
		});
	partitions = try_remove_sole_extended( partitions );
	}
    return( partitions );
    }


list<map> remove_one_partition_vm( map disk )
    {
    list<map> partitions = disk["partitions"]:[];
    list<map> pl = filter( map p, partitions,
			   ``( p["linux"]:false && size(p["mount"]:"")==0 &&
			       !p["delete"]:false &&
			       Storage::CanDelete( p, disk, false )));
    if( size(pl)>0 )
	{
	pl = sort( map a, map b, pl, 
		   ``(a["size_k"]:0<b["size_k"]:0));
	pl = (list<map>) union( 
	    filter( map p, pl, ``(p["type"]:`primary==`logical) ),
	    filter( map p, pl, ``(p["type"]:`primary!=`logical) ));
	pl = (list<map>) union( 
	    filter( map p, pl, ``(p["used_by_type"]:`UB_NONE==`UB_NONE) ),
	    filter( map p, pl, ``(p["used_by_type"]:`UB_NONE!=`UB_NONE) ));
	y2milestone( "remove_one_partition_vm pl %1", pl );

	integer nr = 0;

	partitions = 
	    maplist( map p, partitions,
		``{
		if( p["linux"]:false && !p["delete"]:false &&
		    size(p["mount"]:"")==0 &&
		    p["device"]:""==pl[0,"device"]:"" &&
		    Storage::CanDelete( p, disk, false ))
		    {
		    p["delete"] = true;
		    nr = p["nr"]:0;
		    y2milestone( "remove_one_partition_vm p %1", p );
		    }
		return( p );
		});
	if( nr>disk["max_primary"]:4 )
	    {
	    partitions = 
		maplist( map p, partitions,
		    ``{
		    if( !p["delete"]:false && p["nr"]:0>nr )
			{
			p["nr"] = p["nr"]:0-1;
			p["device"] = Storage::GetDeviceName( disk["device"]:"",
			                                      p["nr"]:0 );
			y2milestone( "remove_one_partition_vm ren %1", p );
			}
		    return( p );
		    });
	    }
	partitions = try_remove_sole_extended( partitions );
	}
    return( partitions );
    }


map remove_used_by( map tg, string disk )
    {
    string uby = tg[disk,"used_by_device"]:"";
    y2milestone( "remove_used_by disk %1 uby %2", disk, uby );
    if( size(uby)>0 )
	{
	if( haskey( tg, uby ))
	    {
	    tg[uby, "delete"] = true;
	    y2milestone( "remove_used_by uby %1", tg[uby]:$[] );
	    }
	}
    return( tg );
    }


    map<string, any> get_inst_proposal(map<string, map> target)
    {
    y2milestone( "get_inst_proposal start" );
    flex_init_swapable( target );
    map<string, any> ret = $[];
    target = AddWinInfo(target);
    ret["target"] = target;
    map root = $[ "mount" : "/", "increasable" : true,
                  "fsys" : Partitions::DefaultFs(), "size" : 0 ];
    map opts = GetControlCfg();
    list<string> ddev = get_disk_try_list( target, true );
    string sol_disk = "";
    list modes = [ `free, `reuse, `remove, `resize, `desparate ];
    map<string,boolean> valid = $[];
    map<string,list> size_mb = listmap( string s, ddev, ``($[s:[]]));
    map solution = listmap( string s, ddev, ``($[s:[]]));
    target = prepare_part_lists( ddev, target );
    symbol mode = `free;
    while( mode != `end && size(sol_disk)==0 )
	{
	if( mode == `free || mode == `desparate )
	    {
	    valid = listmap( string s, ddev, ``($[s:true]));
	    if( mode == `desparate )
		{
		ddev = get_disk_try_list( target, false );
		valid = listmap( string s, ddev, ``($[s:true]));
		target = prepare_part_lists( ddev, target );
		foreach( string s, ddev,
		    ``{
		    target[s,"partitions"] = 
			remove_p_settings( target[s,"partitions"]:[], [] );
		    target[s,"partitions"] = 
			maplist( map p, target[s,"partitions"]:[],
			    ``{
			    if( !contains( Partitions::do_not_delete, 
					   p["fsid"]:0 ) &&
				Storage::CanDelete( p, target[s]:$[], false))
				{
				if( usable_for_win_resize(p,false) )
				    p["dtxt"] = _("Resize impossible due to inconsistent fs. Try checking fs under Windows.");
				p["delete"] = true;
				}
			    return( p );
			    });
		    });
		}
	    }
	else if( mode == `reuse )
	    {
	    valid = listmap( string s, ddev, 
		``{
		if( find( map p, target[s,"partitions"]:[], 
		          ``(p["linux"]:false && size(p["mount"]:"")==0 &&
			     !p["delete"]:false &&
			     Storage::CanEdit(p,false))) != nil )
		    return( $[s:true] );
		else
		    return( $[s:false] );
		});
	    }
	else if( mode == `remove )
	    {
	    valid = listmap( string s, ddev, 
		``{
		if( find( map p, target[s,"partitions"]:[], 
		          ``(p["linux"]:false && size(p["mount"]:"")==0 &&
			     !p["delete"]:false &&
			     Storage::CanDelete(p,target[s]:$[],false))) != nil )
		    return( $[s:true] );
		else
		    return( $[s:false] );
		});
	    foreach( string s, ddev,
		``{
		target[s,"partitions"] = 
		    remove_p_settings( target[s,"partitions"]:[], 
				       ["/", "/home"] );
		});
	    foreach( string s, filter( string d, ddev, ``(valid[d]:false)),
		``{
		target[s,"partitions"] = 
		    remove_one_partition( target[s]:$[] );
		});
	    }
	else if( mode == `resize )
	    {
	    valid = listmap( string s, ddev, 
		``{
		if( find( map p, target[s,"partitions"]:[], 
		          ``(usable_for_win_resize(p,true))) != nil )
		    return( $[s:true] );
		else
		    return( $[s:false] );
		});
	    foreach( string s, filter( string d, ddev, ``(valid[d]:false)),
		``{
		list<map> pl = filter( map p, target[s,"partitions"]:[],
				       ``(usable_for_win_resize(p,true)));
		if( size(pl)>0 )
		    {
		    pl = sort( map a, map b, pl, 
			       ``(a["size_k"]:0>b["size_k"]:0));
		    target[s,"partitions"] = 
			maplist( map p, target[s,"partitions"]:[],
			    ``{
			    if( usable_for_win_resize(p,true) &&
				p["device"]:""==pl[0,"device"]:"" )
			    {
				integer cs = target[s,"cyl_size"]:1;
				p["resize"] = true;
				p["region",1] = 
				        (p["winfo","new_size"]:0+cs-1) / cs ;
				p["win_max_length"] =
				        (p["winfo","max_win_size"]:0+cs-1) / cs;
				// nil means partition to be defined
				if (Storage::resize_partition == nil)
				{
				    Storage::resize_partition = p["device"]:"";
				    Storage::resize_partition_data = p;
				    Storage::resize_cyl_size = cs;
				}
				// if partitions match, override proposal with stored data
				else if (Storage::resize_partition == p["device"]:"" && Storage::resize_partition != "")
				{
				    p = Storage::resize_partition_data;
				}
			    }
			    return( p );
			    });
		    y2milestone( "get_inst_proposal res parts %1", 
		                 target[s,"partitions"]:[] );
		    }
		});
	    }
	y2milestone( "get_inst_proposal mode %1 valid %2", mode, valid );
	foreach( string s, filter( string d, ddev, ``(valid[d]:false)),
	    ``{
	    map conf = $[ "partitions" : [] ];
	    map disk = target[s]:$[];
	    list<map> p = can_boot_reuse( s, disk["label"]:"msdos",
	                                  need_boot(disk),
					  disk["max_primary"]:4,
					  disk["partitions"]:[] );
	    disk["partitions"] = 
		special_boot_proposal_prepare( disk["partitions"]:[] );
	    boolean have_home = false;
	    boolean have_root = false;
	    boolean have_boot = (!(Partitions::EfiBoot() || Arch::ia64()) || mode!=`free) && size(p)>0;
	    if( have_boot )
	        disk["partitions"] = p;
	    map r = can_swap_reuse( s, disk["partitions"]:[], target );
	    boolean have_swap = size(r)>0;
	    y2milestone( "get_inst_proposal have_boot %1 have_swap %2", 
	                 have_boot, have_swap );
	    if( haskey( r, "partitions" ))
		disk["partitions"] =  r["partitions"]:[];
	    else if( haskey( r, "targets" ))
		target = r["targets"]:$[];
	    list<integer> swap_sizes = [];
	    integer avail_size = get_usable_size_mb(disk,mode==`reuse);
	    y2milestone( "get_inst_proposal disk %1 mode %2 avail %3", 
	                 s, mode, avail_size );
	    if( avail_size>0 )
		{
		if( mode == `reuse )
		    {
		    list<map> parts = disk["partitions"]:[];
		    if( GetProposalHome() )
			{
			if( avail_size > opts["home_limit"]:0 )
			    parts = can_mp_reuse( "/home", 4*1024, 0, parts );
			else
			    parts = can_mp_reuse( "/", opts["root_base"]:0, 0, 
			                          parts );
			}
		    if( size(parts)>0 && avail_size > opts["home_limit"]:0 )
			{
			integer mx = 0;
			if( GetProposalHome() )
			    mx = opts["root_max"]:0;
			parts = can_mp_reuse( "/", opts["root_base"]:0, 
					      mx, parts );
			}
		    if( size(parts)>0 )
			{
			have_home = avail_size > opts["home_limit"]:0;
			have_root = true;
			disk["partitions"] = parts;
			}
		    y2milestone( "get_inst_proposal reuse have_home %1 have_root %2",
		                 have_home, have_root );
		    if( have_home && have_root )
			y2milestone( "get_inst_proposal reuse parts %1",
			             disk["partitions"]:[] );
		    }
		if( !have_swap )
		    {
		    swap_sizes = get_swap_sizes( avail_size );
		    map swap = $[ "mount" : "swap", "increasable" : true, 
				  "fsys" : `swap, "maxsize" : 2*1024*1024*1024,
				  "size"  : swap_sizes[0]:256*1024*1024 ];
		    conf["partitions"] = add( conf["partitions"]:[], swap );
		    }
		if( !have_root )
		    conf["partitions"] = add( conf["partitions"]:[], root );
		map old_root = $[];
		if( !have_home && GetProposalHome() &&
		    opts["home_limit"]:0 < avail_size )
		    {
		    map home = $[ "mount" : "/home", "increasable" : true, 
				  "fsys" : Partitions::DefaultFs(), 
				  "size" : 512*1024*1024, 
				  "pct" : 100-opts["root_percent"]:40 ];
		    conf["partitions"] = 
			maplist( map p, conf["partitions"]:[],
			    ``{
			    if( p["mount"]:""=="/" )
				{
				old_root = p;
				p["pct"] = opts["root_percent"]:40;
				p["maxsize"] = opts["root_max"]:0*1024*1024;
				p["size"] = opts["root_base"]:0*1024*1024;
				}
			    return( p );
			    });
		    conf["partitions"] = add( conf["partitions"]:[], home );
		    }
		map ps1 = do_flexible_disk_conf( disk, conf, have_boot, 
						 mode==`reuse );
		if( size(old_root)>0 && !ps1["ok"]:false )
		    {
		    conf["partitions"] = 
			filter( map p, conf["partitions"]:[],
				``(p["mount"]:""!="/home" && 
				   p["mount"]:""!="/"));
		    conf["partitions"] = add( conf["partitions"]:[], old_root );
		    ps1 = do_flexible_disk_conf( disk, conf, have_boot, 
						 mode==`reuse );
		    }
		if( !have_swap )
		    {
		    integer diff = swap_sizes[0]:256 - swap_sizes[1]:256;
		    if( diff<0 )
			diff = -diff;
		    y2milestone( "get_inst_proposal diff:%1 ps1 ok:%2", 
				 diff, ps1["ok"]:false );
		    if( (!ps1["ok"]:false && diff>0) || diff>100 )
			{
			conf["partitions",0,"size"] = 
			    swap_sizes[1]:256*1024*1024;
			map ps2 = do_flexible_disk_conf( disk, conf, have_boot, 
							 mode==`reuse );
			y2milestone( "get_inst_proposal ps2 ok:%1", 
				     ps2["ok"]:false );
			if( ps2["ok"]:false )
			    {
			    map rp1 = find( map p, ps1["disk","partitions"]:[], 
					    ``(!p["delete"]:false && 
					       p["mount"]:""=="/"));
			    map rp2 = find( map p, ps2["disk","partitions"]:[], 
					    ``(!p["delete"]:false && 
					       p["mount"]:""=="/"));
			    y2milestone( "get_inst_proposal rp1:%1", rp1 );
			    y2milestone( "get_inst_proposal rp2:%1", rp2 );
			    if( rp1==nil || 
				(rp2!=nil && rp2["size_k"]:0>rp1["size_k"]:0 ))
				ps1 = ps2;
			    }
			}
		    }
		if( ps1["ok"]:false )
		    {
		    list mb = [ get_mb_sol( ps1, "/" )];
		    if( GetProposalHome() )
			mb = add( mb, get_mb_sol( ps1, "/home" ));
		    if( mb[0]:0+mb[1]:0 > size_mb[s,0]:0 + size_mb[s,1]:0 )
			{
			solution[s] = ps1["disk"]:$[];
			size_mb[s] = mb;
			y2milestone( "get_inst_proposal sol %1 mb %2", 
			             s, size_mb[s]:[] );
			}
		    }
		}
	    });
	integer max_mb = 0;
	string max_disk = "";
	foreach( string s, list mb, size_mb,
	    ``{
	    if( (!GetProposalHome() || mb[1]:0>0 || mode==`resize ) && 
	        mb[1]:0+mb[0]:0 > max_mb )
		{
		max_mb = mb[1]:0+mb[0]:0;
		max_disk = s;
		}
	    });
	y2milestone( "get_inst_proposal max_mb %1 size_mb %2", max_mb, size_mb );
	if( max_mb>0 && size_mb[max_disk,0]:0 > 2*1024 && 
	    ( !GetProposalHome() || size_mb[max_disk,1]:0 > 1*1024 ))
	    {
	    sol_disk = max_disk;
	    }
	y2milestone( "get_inst_proposal mode %1 size_mb %2", mode, size_mb );
	if( size(sol_disk)==0 )
	    {
	    list<boolean> lb = maplist(string s, boolean e, valid, ``(e));
	    if( mode == `free )
		mode = `reuse;
	    else if( mode == `reuse )
		mode = `remove;
	    else if( mode == `remove && find( boolean v, lb, ``(v)) == nil )
		mode = `resize;
	    else if( mode == `resize && find( boolean v, lb, ``(v)) == nil )
		mode = `desparate;
	    else if( mode == `desparate )
		mode = `end;
	    if( mode == `desparate && size(sol_disk)==0 )
		{
		max_mb = 0;
		foreach( string s, list mb, size_mb,
		    ``{
		    if( mb[1]:0+mb[0]:0 > max_mb && mb[0]:0 > 2*1024 )
			{
			max_mb = mb[1]:0+mb[0]:0;
			sol_disk = s;
			}
		    });
		y2milestone( "get_inst_proposal mode %1 sol_disk %2",
		             mode, sol_disk );
		}
	    }
	}
    y2milestone( "get_inst_proposal sol_disk %1", sol_disk );
    if( size(sol_disk)==0 )
	{
	integer max_mb = 0;
	foreach( string s, list mb, size_mb,
	    ``{
	    if( mb[1]:0+mb[0]:0>max_mb && mb[0]:0 > 512 )
		{
		max_mb = mb[1]:0+mb[0]:0;
		sol_disk = s;
		}
	    });
	y2milestone( "get_inst_proposal sol_disk %1", sol_disk );
	}
    ret["ok"] = size(sol_disk)>0;
    if( ret["ok"]:false )
	{
	ret["target"] = remove_used_by( ret["target"]:$[], sol_disk );
	ret["target",sol_disk] = solution[sol_disk]:$[];
	ret["target"] = Storage::SpecialBootHandling( ret["target"]:$[] );
	y2milestone( "get_inst_proposal sol:%1", ret["target",sol_disk]:$[] );
	}
    y2milestone( "get_inst_proposal ret[ok]:%1", ret["ok"]:false );
    return( ret );
    }


list<map> remove_keys( list<map> pl, list<string> keys )
    {
    pl = maplist( map p, pl,
	``{
	foreach( string k, keys,
	    ``{
	    if( haskey(p,k) )
		p = remove( p, k );
	    });
	return( p );
	});
    return( pl );
    }


map<string,map> remove_mount_points( map<string,map> target )
    {
    foreach( string s, map disk, target,
	``{
	target[s,"partitions"] = 
	    maplist( map p, target[s,"partitions"]:[],
		``{
		if( haskey( p, "mount" ) && 
		    search( p["mount"]:"", "/windows/" )!=0 && 
		    search( p["mount"]:"", "/dos/" )!=0 )
		    p = remove( p, "mount" );
		return( p );
		});
	remove_keys( target[s,"partitions"]:[],
	                                      [ "mount" ] );
	});
    return( target );
    }


map<string,map> remove_vm( map<string,map> tg, string ky )
    {
    y2milestone( "remove_vm key:%1", ky );
    string key = "/dev/" + ky;
    if( haskey( tg, key ))
	{
	tg[key,"delete"] = true;
	tg[key,"partitions"] = 
	    maplist( map p, tg[key,"partitions"]:[],
		     ``{
		     p["delete"] = true;
		     return(p);
		     });
	y2milestone( "remove_vm removed:%1", tg[key]:$[] );
	list<string> dl = tg[key,"devices"]:[];
	foreach( string d, dl, {
	    tg = Storage::DelPartitionData( tg, d, "used_by_type" );
	    tg = Storage::DelPartitionData( tg, d, "used_by_device" );
	});
	}
    return( tg );
    }


string find_vm( map<string,map> target, string ky, integer min_size )
    {
    string ret = "";
    string key = "/dev/" + ky;
    if( GetProposalLvm() && target[key,"lvm2"]:false && 
        target[key,"size_k"]:0 >= min_size )
	ret = key;
    y2milestone( "find_vm key:%1 min_size:%2 ret:%3", ky, min_size, ret );
    return( ret );
    }


boolean did_remove_vg( list<map> partitions, string vg )
    {
    boolean ret=false;
    list<string> ele = [ "/dev/" + vg, "/dev/lvm/" + vg, "/dev/lvm2/" + vg ];
    foreach( map p, partitions, 
	``{
	if( !ret && p["delete"]:false && contains( ele, p["used_by_device"]:"" ))
	    ret = true;
	});
    y2milestone( "did_remove_vg vg:%1 ret:%2", vg, ret );
    return( ret );
    }


integer sizek_to_pe( integer pek, integer pebyte, boolean pvcreate )
    {
    integer ret = (pek-(pvcreate?500:0)) / (pebyte/1024);
    y2milestone( "sizek_to_pe pek %1 pebyte %2 pvcreate %3 ret %4", 
                 pek, pebyte, pvcreate, ret );
    return( ret );
    }


integer pe_to_sizek( integer pe, integer pebyte )
    {
    integer ret = pe * (pebyte/1024);
    y2milestone( "pe_to_sizek pe %1 pebyte %2 ret %3", pe, pebyte, ret );
    return( ret );
    }


map extend_vm( map vg, string key, map disk )
    {
    y2milestone( "extend_vm key %1 vg %2", key, vg );
    y2milestone( "extend_vm disk %1", disk );
    list devs = [];
    integer num_pe = vg["pe_free"]:0;
    foreach( map p, disk["partitions"]:[],
	``{
	if( p["vg"]:""==key )
	    {
	    devs = add( devs, p["device"]:"" );
	    num_pe = num_pe + 
	             sizek_to_pe( p["size_k"]:0, vg["pesize"]:1024, true );
	    };
	});
    y2milestone( "extend_vm num_pe %1 devs %2", num_pe, devs );
    vg["devices_add"] = devs;
    vg["pe_free"] = num_pe;
    vg["size_k"] = pe_to_sizek( num_pe, vg["pesize"]:0 );
    y2milestone( "extend_vm ret %1", vg );
    return( vg );
    }


map create_vm( string key, map disk )
    {
    y2milestone("create_vm key:%1 disk:%2", key, disk);
    map ret = $[ "type" : `CT_LVM,
                 "name" : key,
		 "device" : "/dev/" + key,
                 "lvm2" : true,
		 "create" : true,
		 "partitions" : [],
		 "pesize" : 4*1024*1024 ];
    list devs = [];
    integer num_pe = 0;
    foreach( map p, disk["partitions"]:[],
	``{
	if( p["vg"]:""==key && !p["delete"]:false )
	    {
	    devs = add( devs, p["device"]:"" );
	    num_pe = num_pe + 
	             sizek_to_pe( p["size_k"]:0, ret["pesize"]:1024, true );
	    };
	});
    y2milestone( "create_vm num_pe %1 devs %2", num_pe, devs );
    ret["devices_add"] = devs;
    ret["pe_free"] = num_pe;
    ret["size_k"] = pe_to_sizek( num_pe, ret["pesize"]:0 );
    y2milestone( "create_vm ret %1", ret );
    return( ret );
    }


map modify_vm( map vm, map opts, boolean need_swap )
    {
    y2milestone( "modify_vm swap %1 start %2", need_swap, vm );
    y2milestone( "modify_vm opts %1", opts );
    map ret = vm;
    integer free = ret["pe_free"]:0;
    integer pe = ret["pesize"]:1024;
    integer swsize = 0;
    list<integer> swlist = [];
    if( need_swap )
	{
	swlist = get_swap_sizes( free );
	swsize = (swlist[1]:0>swlist[0]:0) ? swlist[1]:0 : swlist[0]:0;
	}
    y2milestone( "modify_vm swsize %1", swsize );
    map swap = find( map p, ret["partitions"]:[], ``(p["name"]:""=="swap"));
    map root = find( map p, ret["partitions"]:[], ``(p["name"]:""=="root"));
    map home = find( map p, ret["partitions"]:[], ``(p["name"]:""=="home"));
    y2milestone( "modify_vm swap %1 root %2 home %3", swap, root, home );
    if( root!=nil && root["size_k"]:0<1024*1024 )
	{
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
	    ``{
	    if( p["name"]:"" == "root" )
		{
		p["delete"] = true;
		free = free + sizek_to_pe( p["size_k"]:0, pe, false );
		y2milestone( "modify_vm remove root %1", p );
		}
	    return(p);
	    });
	y2milestone( "modify_vm pe free %1", free );
	root = nil;
	}
    ret["partitions"] = sort( map a, map b, ret["partitions"]:[], 
                              ``(a["size_k"]:0>b["size_k"]:0));
    list keep = [ "root", "home", "swap" ];
    integer root_pe = sizek_to_pe( opts["root_base"]:0*1024, pe, false );
    y2milestone( "modify_vm pe free %1 root %2", free, root_pe );
    map m = find( map p, ret["partitions"]:[],
		  ``(!p["delete"]:false && !contains(keep,p["name"]:"")));
    while( root==nil && free<root_pe && m!=nil )
	{
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
	    ``{
	    if( p["name"]:"" == m["name"]:"" )
		{
		p["delete"] = true;
		free = free + sizek_to_pe( p["size_k"]:0, pe, false );
		}
	    return(p);
	    });
	y2milestone( "modify_vm pe free %1 root %2 del %3", free, root_pe, m );
	m = find( map p, ret["partitions"]:[],
		  ``(!p["delete"]:false && !contains(keep,p["name"]:"")));
	}
    if( root==nil && free<root_pe && swap!=nil && swsize==0 )
	{
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
	    ``{
	    if( p["name"]:"" == "swap" )
		{
		p["delete"] = true;
		free = free + sizek_to_pe( p["size_k"]:0, pe, false );
		}
	    return(p);
	    });
	swap = nil;
	y2milestone( "modify_vm pe free %1 root %2", free, root_pe );
	}
    if( root==nil && free<root_pe && swsize>0 )
	{
	swsize = (swlist[1]:0<swlist[0]:0) ? swlist[1]:0 : swlist[0]:0;
	y2milestone( "modify_vm new swsize %1", swsize );
	}
    if( root==nil && free<root_pe && swap!=nil && swsize<swap["size_k"]:0/1024 )
	{
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
	    ``{
	    if( p["name"]:"" == "swap" )
		{
		p["delete"] = true;
		free = free + sizek_to_pe( p["size_k"]:0, pe, false );
		}
	    return(p);
	    });
	swap = nil;
	y2milestone( "modify_vm pe free %1 root %2", free, root_pe );
	}
    integer swap_pe = 0;
    integer home_pe = 0;
    if( swap==nil && swsize>0 )
	swap_pe = sizek_to_pe( swsize*1024, pe, false );
    if( free<root_pe+swap_pe )
	{
	y2milestone( "modify_vm pe free %1 root %2 swap %3", 
	             free, root_pe, swap_pe );
	if( root==nil && swap_pe>free )
	    {
	    swap_pe = free;
	    free = 0;
	    }
	else if( swap_pe==0 )
	    {
	    root_pe = free;
	    free = 0;
	    }
	else
	    {
	    swap_pe = swap_pe*free/(root_pe+swap_pe);
	    root_pe = free-swap_pe;
	    free = 0;
	    }
	y2milestone( "modify_vm pe free %1 root %2 swap %3", 
	             free, root_pe, swap_pe );
	}
    else
	{
	free = free-swap_pe;
	y2milestone( "modify_vm pe free %1 root %2 swap %3", 
	             free, root_pe, swap_pe );
	if( home==nil && GetProposalHome() &&
	    free>sizek_to_pe( opts["home_limit"]:0*1024, pe, false ) )
	    {
	    integer tmp = free * opts["root_percent"]:40 / 100;
	    if( tmp>root_pe )
		root_pe=tmp;
	    tmp = sizek_to_pe( opts["root_max"]:0*1024, pe, false );
	    if( root_pe>tmp )
		root_pe=tmp;
	    free = free-root_pe;
	    home_pe = free;
	    tmp = sizek_to_pe( opts["home_max"]:0*1024, pe, false );
	    if( home_pe>tmp )
		home_pe = tmp;
	    free = free-home_pe;
	    y2milestone( "modify_vm pe free %1 root %2 home %3", 
			 free, root_pe, home_pe );
	    }
	else 
	    {
	    integer tmp = sizek_to_pe( opts["root_max"]:0*1024, pe, false );
	    root_pe = free;
	    if( root_pe>tmp )
		root_pe=tmp;
	    free = free-root_pe;
	    }
	}
    y2milestone( "modify_vm pe free %1 root %2 swap %3 home %4", 
		 free, root_pe, swap_pe, home_pe );
    if( root==nil && root_pe>0 )
	{
	map p = $[ "create":true, "name" : "root", 
		   "device" : ret["device"]:"" + "/root",
	           "size_k" : pe_to_sizek(root_pe,pe) ];
        p = Storage::SetVolOptions( p, "/", Partitions::DefaultFs(),
				    "", "", "" );
	y2milestone( "modify_vm created %1", p );
	ret["partitions"] = add( ret["partitions"]:[], p );
	}
    else if( root!=nil )
	{
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
	    ``{
	    if( p["name"]:"" == "root" )
		{
		p = Storage::SetVolOptions( p, "/", Partitions::DefaultFs(),
					    "", "", "" );
		y2milestone( "modify_vm reuse %1", p );
		}
	    return(p);
	    });
	}
    if( swap==nil && swap_pe>0 )
	{
	map p = $[ "create":true, "name" : "swap", 
		   "device" : ret["device"]:"" + "/swap",
	           "size_k" : pe_to_sizek(swap_pe,pe) ];
	p = Storage::SetVolOptions( p, "swap", `swap,
				    "", "", "" );
	y2milestone( "modify_vm created %1", p );
	ret["partitions"] = add( ret["partitions"]:[], p );
	}
    else if( swap!=nil )
	{
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
	    ``{
	    if( p["name"]:"" == "swap" )
		{
		p = Storage::SetVolOptions( p, "swap", `swap,
					    "", "", "" );
		y2milestone( "modify_vm reuse %1", p );
		}
	    return(p);
	    });
	}
    if( home==nil && home_pe>0 )
	{
	map p = $[ "create":true, "name" : "home", 
		   "device" : ret["device"]:"" + "/home",
	           "size_k" : pe_to_sizek(home_pe,pe) ];
	p = Storage::SetVolOptions( p, "/home", Partitions::DefaultFs(),
				    "", "", "" );
	y2milestone( "modify_vm created %1", p );
	ret["partitions"] = add( ret["partitions"]:[], p );
	}
    else if( home!=nil )
	{
	ret["partitions"] = maplist( map p, ret["partitions"]:[],
	    ``{
	    if( p["name"]:"" == "home" )
		{
		p = Storage::SetVolOptions( p, "/home", Partitions::DefaultFs(),
					    "", "", "" );
		y2milestone( "modify_vm reuse %1", p );
		}
	    return(p);
	    });
	}
    y2milestone( "modify_vm ret %1", ret );
    return( ret );
    }


    map<string, any> get_inst_prop_vm(map<string, map> target, string key)
    {
    y2milestone( "get_inst_prop_vm start key %1", key );
    map<string, any> ret = $[];
    map opts = GetControlCfg();
    string vg_key = find_vm( target, key, opts["root_base"]:0*1024 );
    target = remove_mount_points(target);
    if( size(vg_key)==0 )
	target = remove_vm(target,key);
    target = AddWinInfo(target);
    ret["target"] = target;
    map boot = $[ "mount" : Partitions::BootMount(), 
                  "size" : Partitions::MinimalNeededBootsize(),
                  "fsys" : Partitions::DefaultBootFs(),
		  "id" : Partitions::FsidBoot(),
		  "max_cyl" : Partitions::BootCyl() ];
    if( Partitions::BootPrimary() )
	{
	boot["primary"] = true;
	}
    list<string> ddev = get_disk_try_list( target, true );
    string sol_disk = "";
    list modes = [ `free, `remove, `resize, `desparate ];
    map<string,boolean> valid = $[];
    map<string,integer> size_mb = listmap( string s, ddev, ``($[s:0]));
    map<string,boolean> keep_vg = $[];
    map solution = listmap( string s, ddev, ``($[s:[]]));
    target = prepare_part_lists( ddev, target );
    symbol mode = `free;
    while( mode != `end && size(sol_disk)==0 )
	{
	if( mode == `free || mode == `desparate )
	    {
	    valid = listmap( string s, ddev, ``($[s:true]));
	    if( mode == `desparate )
		{
		ddev = get_disk_try_list( target, false );
		valid = listmap( string s, ddev, ``($[s:true]));
		target = prepare_part_lists( ddev, target );
		foreach( string s, ddev,
		    ``{
		    target[s,"partitions"] = 
			remove_p_settings( target[s,"partitions"]:[], [] );
		    target[s,"partitions"] = 
			maplist( map p, target[s,"partitions"]:[],
			    ``{
			    if( !contains( Partitions::do_not_delete, 
					   p["fsid"]:0 ) &&
				Storage::CanDelete( p, target[s]:$[], false))
				{
				if( usable_for_win_resize(p,false) )
				    p["dtxt"] = _("Resize impossible due to inconsistent fs. Try checking fs under Windows.");
				p["delete"] = true;
				}
			    return( p );
			    });
		    });
		}
	    }
	else if( mode == `remove )
	    {
	    valid = listmap( string s, ddev, 
		``{
		if( find( map p, target[s,"partitions"]:[], 
		          ``(p["linux"]:false && size(p["mount"]:"")==0 &&
			     !p["delete"]:false &&
			     Storage::CanDelete(p,target[s]:$[],false))) != nil )
		    return( $[s:true] );
		else
		    return( $[s:false] );
		});
	    foreach( string s, filter( string d, ddev, ``(valid[d]:false)),
		``{
		target[s,"partitions"] = 
		    remove_one_partition_vm( target[s]:$[] );
		});
	    }
	else if( mode == `resize )
	    {
	    valid = listmap( string s, ddev, 
		``{
		if( find( map p, target[s,"partitions"]:[], 
			``(usable_for_win_resize(p,true))) != nil )
		    return( $[s:true] );
		else
		    return( $[s:false] );
		});
	    foreach( string s, filter( string d, ddev, ``(valid[d]:false)),
		``{
		list<map> pl = filter( map p, target[s,"partitions"]:[],
				       ``(usable_for_win_resize(p,true)));
		if( size(pl)>0 )
		    {
		    pl = sort( map a, map b, pl, 
			       ``(a["size_k"]:0>b["size_k"]:0));
		    target[s,"partitions"] = 
			maplist( map p, target[s,"partitions"]:[], ``{
			    if( usable_for_win_resize(p,true) &&
				p["device"]:""==pl[0,"device"]:"" )
			    {
				integer cs = target[s,"cyl_size"]:1;
				p["resize"] = true;
				p["region",1] = 
				        (p["winfo","new_size"]:0+cs-1) / cs ;
				p["win_max_length"] =
				        (p["winfo","max_win_size"]:0+cs-1) / cs;
				// nil means partition to be defined
				if (Storage::resize_partition == nil)
				{
				    Storage::resize_partition = p["device"]:"";
				    Storage::resize_partition_data = p;
				    Storage::resize_cyl_size = cs;
				}
				// if partitions match, override proposal with stored data
				else if (Storage::resize_partition == p["device"]:"" && Storage::resize_partition != "")
				{
				    p = Storage::resize_partition_data;
				}
			    }
			    return( p );
			    });
		    y2milestone( "get_inst_prop_vm res parts %1", 
		                 target[s,"partitions"]:[] );
		    }
		});
	    }
	y2milestone( "get_inst_prop_vm mode %1 valid %2", mode, valid );
	foreach( string s, filter( string d, ddev, ``(valid[d]:false)),
	    ``{
	    map disk = target[s]:$[];
	    map conf = $[ "partitions" : [] ];
	    list<map> p = can_boot_reuse( s, disk["label"]:"msdos", true,
					  disk["max_primary"]:4,
					  disk["partitions"]:[] );
	    disk["partitions"] = 
		special_boot_proposal_prepare( disk["partitions"]:[] );
	    boolean have_boot = size(p)>0;
	    if( have_boot )
	        disk["partitions"] = p;
	    string vg = vg_key;
	    if( size(vg)>0 && did_remove_vg( disk["partitions"]:[], key ))
		vg = "";
	    map ps = do_vm_disk_conf( disk, have_boot?($[]):boot, vg, key );
	    if( ps["ok"]:false )
		{
		integer mb = get_vm_sol( ps );
		if( mb-size_mb[s]:0>size_mb[s]:0/40 )
		    {
		    y2milestone( "get_inst_prop_vm new sol %1 old %2 new %3",
		                 s, size_mb[s]:0, mb );
		    solution[s] = ps["disk"]:$[];
		    size_mb[s] = mb;
		    }
		if( size(vg)>0 && !keep_vg[s]:false )
		    keep_vg[s] = true;
		}
	    });
	y2milestone( "get_inst_prop_vm size_mb %1 keep_vg %2", 
	             size_mb, keep_vg );
	integer max_mb = 0;
	string max_disk = "";
	string keep_disk = "";
	foreach( string s, integer mb, size_mb,
	    ``{
	    if( mb > max_mb )
		{
		max_mb = mb;
		max_disk = s;
		}
	    });
	foreach( string s, boolean keep, keep_vg,
	    ``{
	    if( size(keep_disk)==0 && keep )
		{
		keep_disk = s;
		}
	    });
	if( size(keep_disk)>0 )
	    {
	    sol_disk = keep_disk;
	    }
	else if( max_mb>0 && size_mb[max_disk]:0 > opts["vm_want"]:(20*1024) )
	    {
	    sol_disk = max_disk;
	    vg_key = "";
	    }
	y2milestone( "get_inst_prop_vm mode %1 sol_disk %2", mode, sol_disk );
	if( size(sol_disk)==0 )
	    {
	    list<boolean> lb = maplist(string s, boolean e, valid, ``(e));
	    if( mode == `free )
		mode = `remove;
	    else if( mode == `remove && find( boolean v, lb, ``(v)) == nil )
		mode = `resize;
	    else if( mode == `resize && find( boolean v, lb, ``(v)) == nil )
		mode = `desparate;
	    else if( mode == `desparate )
		mode = `end;
	    }
	}
    y2milestone( "get_inst_prop_vm sol_disk %1", sol_disk );
    if( size(sol_disk)==0 )
	{
	integer max_mb = 0;
	foreach( string s, integer mb, size_mb,
	    ``{
	    if( mb>max_mb )
		{
		max_mb = mb;
		sol_disk = s;
		}
	    });
	y2milestone( "get_inst_prop_vm sol_disk %1", sol_disk );
	}
    ret["ok"] = size(sol_disk)>0;
    if( ret["ok"]:false )
	{
	map r = can_swap_reuse( sol_disk, solution[sol_disk,"partitions"]:[], 
	                        ret["target"]:$[] );
	if( haskey( r, "partitions" ))
	    solution[sol_disk,"partitions"] = r["partitions"]:[];
	else if( haskey( r, "targets" ))
	    ret["target"] = r["targets"]:$[];
	ret["target"] = remove_used_by( ret["target"]:$[], sol_disk );
	if( size(vg_key)==0 )
	    {
	    vg_key = "/dev/" + key;
	    map vg = ret["target",vg_key]:$[];
	    vg = union( vg, create_vm( key, solution[sol_disk]:$[] ));
	    if( size(vg["devices"]:[])>0 )
		{
		vg = remove( vg, "devices" );
		}
	    y2milestone( "get_inst_prop_vm vkey %1", vg );
	    ret["target",vg_key] = vg;
	    }
	else
	    {
	    map vg = ret["target",vg_key]:$[];
	    vg = extend_vm( vg, key, solution[sol_disk]:$[] );
	    ret["target",vg_key] = vg;
	    }
	ret["target",vg_key] = modify_vm( ret["target",vg_key]:$[], opts,
	                                  size(r)==0 );
	ret["target",sol_disk] = solution[sol_disk]:$[];
	ret["target"] = Storage::SpecialBootHandling( ret["target"]:$[] );
	y2milestone( "get_inst_prop_vm sol:%1", ret["target",sol_disk]:$[] );
	}
    y2milestone( "get_inst_prop_vm ret[ok]:%1", ret["ok"]:false );
    return( ret );
    }


    global map<string, any> get_proposal_vm(map<string, map> target, string key, map disk)
    {
    string ddev = disk["device"]:"";
    y2milestone("get_proposal_vm ddev:%1 vg:%2 home:%3 lvm:%4 encrypt:%5", ddev,
		key, GetProposalHome(), GetProposalLvm(), GetProposalEncrypt());
    map<string, any> ret = $[];
    map opts = GetControlCfg();
    target = remove_mount_points(target);
    target = remove_vm(target,key);
    ret["target"] = target;
    map boot = $[ "mount" : Partitions::BootMount(), 
                  "size" : Partitions::MinimalNeededBootsize(),
                  "fsys" : Partitions::DefaultBootFs(),
		  "id" : Partitions::FsidBoot(),
		  "max_cyl" : Partitions::BootCyl() ];
    if( Partitions::BootPrimary() )
	{
	boot["primary"] = true;
	}
    map conf = $[ "partitions" : [] ];
    list<map> p = can_boot_reuse( ddev, disk["label"]:"msdos", true,
				  disk["max_primary"]:4,
				  disk["partitions"]:[] );
    disk["partitions"] = special_boot_proposal_prepare( disk["partitions"]:[] );
    boolean have_boot = size(p)>0;
    if( have_boot )
	disk["partitions"] = p;
    map ps = do_vm_disk_conf( disk, have_boot?($[]):boot, "", key );
    ret["ok"] = ps["ok"]:false;
    if( ret["ok"]:false )
	{
	disk = ps["disk"]:$[];
	map r = can_swap_reuse( ddev, disk["partitions"]:[], 
	                        ret["target"]:$[] );
	if( haskey( r, "partitions" ))
	    disk["partitions"] = r["partitions"]:[];
	else if( haskey( r, "targets" ))
	    ret["target"] = r["targets"]:$[];
	ret["target"] = remove_used_by( ret["target"]:$[], ddev );
	string vg_key = "/dev/" + key;
	map vg = ret["target",vg_key]:$[];
	vg = union( vg, create_vm( key, disk ));
	if( size(vg["devices"]:[])>0 )
	    {
	    vg = remove( vg, "devices" );
	    }
	y2milestone( "get_proposal_vm vkey %1", vg );
	ret["target",vg_key] = modify_vm( vg, opts, size(r)==0 );
	ret["target",ddev] = disk;
	ret["target"] = EncryptDevices(ret["target"]:$[], vg_key);
	ret["target"] = Storage::SpecialBootHandling( ret["target"]:$[] );
	y2milestone( "get_proposal_vm sol:%1", disk );
	}
    y2milestone("get_proposal_vm ret:%1", ret);
    return ret;
    }


    global map<string, any> get_inst_prop(map<string, map> target)
    {
    map<string, any> ret = $[];
    string vg = GetProposalVM();
    y2milestone("get_inst_prop vg:%1 home:%2 lvm:%3 encypt:%4", vg, 
		GetProposalHome(), GetProposalLvm(), GetProposalEncrypt());
    if (isempty(vg))
	{
        if( has_flex_proposal() )
	    ret = do_proposal_flexible( target );
	else
	    ret = get_inst_proposal( target );
	}
    else
	{
	y2milestone( "target:%1", target );
	ret = get_inst_prop_vm( target, vg );
	ret["target"] = EncryptDevices(ret["target"]:$[], "/dev/" + vg);
	}
    y2milestone("get_inst_prop ret:%1", ret);
    return ret;
    }

}

ACC SHELL 2018