ACC SHELL
/**
* Module: inst_resize_ui.ycp
*
* Authors: Thomas Roelz <tom@suse.de>
* Stefan Hundhammer <sh@suse.de>
*
* Purpose: UI for partition resizing
*
*
* $Id: inst_resize_ui.ycp 58141 2009-07-23 15:05:31Z aschnell $
*/
{
textdomain "storage";
import "Mode";
import "Storage";
import "Popup";
import "Partitions";
import "Wizard";
import "Product";
import "Installation";
include "partitioning/partition_defines.ycp";
// Automatically return if resizing is not requested
//
string do_resize = Storage::GetDoResize(); // minor number (e.g. "1" for /dev/sda1 or "NO")
if( Storage::GetCustomDisplay() || do_resize == "NO" ||
do_resize == "no" || do_resize == "" )
return Storage::GetExitKey();
/////////////////////////////////////////////////////////////////////////
// START: Initialize
/////////////////////////////////////////////////////////////////////////
boolean test_mode = Mode::test ();
boolean demo_mode = Mode::test ();
map<string,map> targets = Storage::GetTargetMap();
string target_is = Storage::GetPartDisk();
// store
//
string win_device = ""; // will be assigned later with e.g. /dev/sda1
map target = $[];
list< map<string,any> > partitions = [];
list< map<string,any> > partitions_new = [];
map<string,any> win_partition = $[];
list<integer> region = [];
symbol ret = `next;
any local_ret = nil;
integer win_used = -1;
integer win_free = -1;
integer min_win_free = -1;
integer new_win_size = -1;
integer linux_size = -1;
integer cyl_size = -1;
integer linux_min = 400; // this is the base value for space calculations (minimum installation)
if ( test_mode ) // not just in demo mode! no HW probe in test mode!
{
win_used = 350;
win_free = 1500;
min_win_free = 50;
linux_size = 800;
linux_min = 400;
}
/////////////////////////////////////////////////////////////////////////
// END: Initialize
/////////////////////////////////////////////////////////////////////////
include "partitioning/auto_part_functions.ycp";
include "partitioning/auto_part_create.ycp";
/////////////////////////////////////////////////////////////////////////
// START: Functions
/////////////////////////////////////////////////////////////////////////
// Displays a popup with the message (can be dismissed with OK).
// After that only `abort or `back is allowed
// Every other user action ==> redisplay message
// Parameter: message to be displayed
// Return : `back or `abort
//
define symbol allow_back_abort_only( string message )
``{
symbol ret = `next;
// Enable back and next buttons independent of the settings
// in installation.ycp so the user has a chance to see the
// popup more than only once.
//
Wizard::EnableNextButton();
Wizard::EnableBackButton();
// To avoid an empty screen behind the popup display the
// message also on the main window
//
term contents = `VBox(
`VStretch(),
`Left( `Label( message ) ),
`VStretch()
);
Wizard::SetContents(
"", // no header in this case
contents,
"", // no help text in this case
(boolean)WFM::Args(0), (boolean)WFM::Args(1)
);
repeat {
Popup::Message( message ); // Display the message
ret= (symbol)UI::UserInput(); // get user input
if ( ret == `abort )
{
if ( ! Popup::ReallyAbort(true) )
{
// user didn't want to abort ==> stay in loop
ret = `dummy;
}
}
else if ( ret == `back )
{
// reset resize flag
Storage::SetDoResize( "NO" );
}
} until ( ret == `abort || ret == `back );
return( ret );
};
// Displays a popup with the message (can be dismissed with OK).
// After that only `abort is allowed
// Every other user action ==> redisplay message
// Parameter: message to be displayed
// Return : nothing
//
define void allow_abort_only( string message )
``{
symbol ret = `next;
// Enable back and next buttons independent of the settings
// in installation.ycp so the user has a chance to see the
// popup more than only once.
//
Wizard::EnableNextButton();
Wizard::EnableBackButton();
// To avoid an empty screen behind the popup display the
// message also on the main window
//
term contents = `VBox(
`VStretch(),
`Left( `Label( message ) ),
`VStretch()
);
Wizard::SetContents(
"", // no header in this case
contents,
"", // no help text in this case
(boolean)WFM::Args(0), (boolean)WFM::Args(1)
);
repeat {
Popup::Message( message ); // Display the message
ret= (symbol)UI::UserInput(); // get user input
if ( ret == `abort )
{
if ( ! Popup::ReallyAbort(false) )
{
// user didn't want to abort ==> stay in loop
ret = `dummy;
}
}
} until ( ret == `abort );
};
// Displays an error message and waits for the user to press OK
// Parameter : nothing
// Return : nothing
//
define void internal_error() ``{
// An internal error has occured. Tell the user that the installation should
// be terminated now and that his hard disk has not been altered yet.
string explanation = _("An internal error has occurred.
You cannot shrink your Windows partition during
installation. Your hard disk has not been altered.
Abort the installation now and shrink your
Windows partition by other means.
");
allow_abort_only( explanation );
};
// Calculate the free space within an extended partition that lies behind
// a certain cylinder value (including this cylinder).
// Parameter : List of partitions containing the extended partition.
// Start cylinder within an extended partition.
// Return : OK - Number of cylinders within the extended partition behind
// the given start cylinder (including it).
// Error - -1
//
define integer get_extended_free( list<map> partitions, integer start_cylinder )
``{
// First get all extended partitions from the list.
//
list mother_part = filter( map pentry, partitions,
``( pentry["type"]:`dummy == `extended ));
// Next from this subset get the partition where the region includes
// the given start cylinder.
//
mother_part = filter( map pentry, partitions,
``{
integer p_start = pentry["region",0]:0;
integer p_length = pentry["region",1]:1;
if ( p_start < start_cylinder &&
p_start + p_length - 1 > start_cylinder ) return true;
else return false;
});
if ( size(mother_part) != 1 ) return -1; // should be exactly one
map ext_part = mother_part[0]:$[]; // the extended partition
integer p_start = ext_part["region",0]:0;
integer p_length = ext_part["region",1]:0;
integer ext_free = p_start + p_length - start_cylinder;
return ext_free;
};
/////////////////////////////////////////////////////////////////////////
// END: Functions
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// START: Preliminary action
/////////////////////////////////////////////////////////////////////////
if ( ! test_mode )
{
// get the selected target device from the target map
//
target = targets[target_is]:$[];
if ( target == $[] )
{
y2error( "Current device <%1> not found in targets.", target_is );
internal_error();
return( `abort ); // abort installation
}
else y2milestone( "Current device <%1> found in targets.", target_is );
// create full device name e.g. /dev/sda1
//
win_device = Storage::GetDeviceName( target_is, tointeger(do_resize) );
// get the cylinder size of this device (used later for region calculation)
//
cyl_size = target["cyl_size"]:-1;
if ( cyl_size == -1 )
{
y2error( "Cylinder size not found in target_data struct." );
internal_error();
return( `abort ); // abort installation
}
else y2milestone( "Cylinder size of target: <%1>", cyl_size );
// get the partition list from the target map
//
partitions = target["partitions"]:[];
if ( partitions == [] )
{
y2error( "Partition list not found in target." );
internal_error();
return( `abort ); // abort installation
}
else y2milestone( "Partition list found in target." );
// Now filter out all "create" and "resize" paritions, they will be re-created.
// (this ensures a 'clean' partition list if this dialogue is re-entered.
// This is actually obsolete since the invention of the targets-restore-mechanism
// but left in for safeness reasons.
//
partitions = filter( map<string,any> pentry, partitions,
``( !pentry["create"]:false &&
!pentry["resize"]:false ));
y2milestone( "Old partition list after cleaning: <%1>", partitions );
// Filter out the windows partition. It will be reinserted after being modified.
// This is the start of the new partitions list.
//
partitions_new = filter( map<string,any> pentry, partitions,
``(pentry["nr"]:-1 != tointeger(do_resize)));
y2milestone( "New partition list without windows partition: <%1>", partitions_new );
// get the windows partition from the old list
//
list p_list = filter( map<string,any> pentry, partitions,
``(pentry["nr"]:-1 == tointeger(do_resize)));
if ( size( p_list ) != 1 ) // there should be only one
{
y2error( "There was not exactly one partition with minor <%1> in the partition list <%2>",
do_resize, partitions );
internal_error();
return( `abort ); // abort installation
}
else
y2milestone( "Partition minor <%1> found in partition list.", do_resize );
// assign the windows partition map
//
win_partition = p_list[0]:$[];
// check if this partition is the right one
//
if( Storage::GetDeviceName( target_is, win_partition["nr"]:-1) !=
win_device )
{
y2error( "Partition from the list <%1> is not the assigned windows device <%2>.",
Storage::GetDeviceName( target_is, win_partition["nr"]:-1 ),
win_device );
internal_error();
return( `abort ); // abort installation
}
else
y2milestone( "Found the Windows partition in the partition list." );
// Check file system type of windows partition
//
if( !Partitions::IsDosPartition( win_partition["fsid"]:0 ) )
{
y2error( "Windows partition <%1> has wrong file system type.", win_device );
internal_error();
return( `abort ); // abort installation
}
else
y2milestone( "Windows partition <%1> has valid file system type - OK.", win_device );
// Get region from win partition
//
region = win_partition["region"]:[];
if( size( region ) != 2 )
{
y2error( "Invalid region <%1> in Windows partition data struct.", region );
internal_error();
return( `abort ); // abort installation
}
else
y2milestone( "Old region <%1> OK in Windows partition data struct.", region );
// mount the partition to execute some checks
//
boolean mount_result =
(boolean)SCR::Execute( .target.mount,
[ win_device, Installation::scr_destdir,
Installation::mountlog ]);
if( !mount_result )
{
y2error( "Current Windows device <%1> could not be mounted. Canceled", win_device );
local_ret = -1;
internal_error();
return( `abort ); // abort installation
}
else
{
y2milestone( "Current Windows device <%1> mounted on %2.", win_device, Installation::scr_destdir );
local_ret = 0;
}
// get usage information for the partition via df
//
list<map> df_result = (list<map>)SCR::Read(.run.df);
SCR::Execute(.target.umount, win_device);
y2debug( ".run.df: %1", df_result );
// filter out headline and other invalid entries
df_result = filter( map part, df_result,
``( substring( part["spec"]:"", 0, 1 ) == "/" ));
foreach( map part, df_result,
``{
if( part["spec"]:"" == win_device ) // find right entry
{
// get the usage values
//
win_used = tointeger(part["used"]:"-1");
win_free = tointeger(part["free"]:"-1");
if( win_used != -1 && win_free != -1 )
{
win_used = win_used / 1024; // MB
win_free = win_free / 1024; // MB
y2milestone( ".run.df: win_used: <%1> win_free:<%2>", win_used, win_free );
}
}
});
if( win_used == -1 || win_free == -1 )
{
y2error( "The sizes for device <%1> could not be examined in df_result <%2>. Canceled",
win_device, df_result );
internal_error();
return( `abort ); // abort installation
}
else
{
// Apply some checks to determine if installing Linux is feasible at all.
//
boolean feasible = true;
// Set minimal free Windows size to 200 MB. Running Windows with
// less disk space is no fun.
//
if ( min_win_free < 200 ) min_win_free = 200;
// If this is more than the free space on the device Windows is already
// overcrowded and Linux shouldn't be installed.
//
if ( min_win_free > win_free ) feasible = false;
// Now see if the so calculated Linux space is big enough
//
if( feasible )
{
if( win_free - min_win_free < linux_min )
{
feasible = false;
}
else
{
// Try to reserve 1.5 GB for linux (default installation).
// Otherwise get as much as possible
//
if ( win_free - min_win_free > 1500 )
linux_size = 1500;
else
linux_size = win_free - min_win_free;
}
}
if( ! feasible )
{
y2error( "Current Windows device <%1> has not enough room for Linux. Canceled", win_device );
y2error( "Space calculation: win_used: <%1> win_free: <%2> min_win_free: <%3> linux_min: <%4> linux_size: <%5>",
win_used, win_free, min_win_free, linux_min, linux_size );
// The Windows partition has not enough free space for Linux. Tell the user the needed amount
// of free space and that he should terminate the installation now.
string explanation = sformat( _("An error has occurred.
Insufficient space is available on the Windows partition to allow for
the minimum Linux installation.
To install Linux, first boot Windows and uninstall some
applications or delete some data.
You need at least %1 MB of free space on the
Windows device, including Windows workspace and
space for %2.
"), linux_min + min_win_free + 10, Product::name ); // 10 MB safety overhead
return( allow_back_abort_only( explanation ) );
}
else
{
y2milestone( "Space calculation: win_used: <%1> win_free:<%2> min_win_free: <%3> linux_min: <%4> linux_size: <%5>",
win_used, win_free, min_win_free, linux_min, linux_size );
}
}
// Do a dosfsck on this partition to assure the file system is clean.
// Do this only if not yet checked.
//
if( !Storage::GetWinDevice() ) // not yet checked
{
// Inform the user that his Windows partition is being checked.
string explanation = _("Checking the file system of your Windows partition
for consistency.
Depending on the size of your Windows partition
and the amount of space used, this may take a while.
");
UI::OpenDialog(`opt(`decorated), `VBox(`Label(explanation)));
string cmd = "/usr/sbin/parted -s " + target_is + " check " + do_resize;
y2milestone ("running: %1", cmd );
local_ret = SCR::Execute(.target.bash, cmd );
UI::CloseDialog();
if( local_ret != 0 )
{
y2error( "Current Windows device <%1> had errors with parted check. Canceled", win_device );
// The file system on the device is faulty. Tell the user he should correct those errors.
string explanation = _("An error has occurred.
Your Windows partition has errors in the file system.
Boot Windows and clear those errors by running
scandisk and defrag.
If the problem occurs again next time, resize your
Windows partition by other means.
");
return( allow_back_abort_only( explanation ) );
}
else // OK
{
y2milestone( "Current Windows device <%1> was OK with parted check.", win_device );
Storage::SetWinDevice( true );
}
} // end of not yet checked
} // not test mode
/////////////////////////////////////////////////////////////////////////
// END: Preliminary action
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// START: GUI
/////////////////////////////////////////////////////////////////////////
boolean test_simple_ui = false; // set to "true" to test non-graphical version
// Unit for parition resizing - currently Megabytes
string unit = _("MB");
// Labels for bar graph. "%1" will be replace with a numeric value.
string bargraph_label_win_used = _("Windows\nUsed\n%1 ") + unit;
// Labels for bar graph. "%1" will be replace with a numeric value.
string bargraph_label_win_free = _("Windows\nFree\n%1 ") + unit;
// Labels for bar graph. "%1" will be replace with a numeric value.
string bargraph_label_linux = _("Linux\n%1 ") + unit;
// Labels for input fields. "%1" will be replaced with the current unit (MB).
string field_label_win_free = sformat( _("Windows Free (%1)"), unit);
// Labels for input fields. "%1" will be replaced with the current unit (MB).
string field_label_linux = sformat( _("Linux (%1)"), unit);
term contents = `Empty();
// Help text for Windows partition resizing -
// common part for both graphical mode (with bar graphs)
// and non-graphical mode (text only).
string helptext = _("<p>
Choose the new size for your Windows partition.
</p>");
// help text (common to both modes), continued
helptext = helptext + _("
<p>
The actual resizing will be performed only after you confirm all your
settings in the last installation dialog. Until then, your Windows
partition will remain untouched.
</p>");
// help text (common to both modes), continued
helptext = helptext + _("
<p>
To skip resizing your Windows partition, press
<b>Back</b>.
</p>
");
if ( UI::HasSpecialWidget(`Slider ) &&
UI::HasSpecialWidget(`BarGraph ) &&
! test_simple_ui )
{
contents = `VBox(
`VStretch(),
// Headline above bar graph that displays current windows partition size
`Left( `Label( _("Now")) ),
`BarGraph(
[ win_used, win_free ],
[
bargraph_label_win_used,
bargraph_label_win_free
]
),
`VStretch(),
// Headline above bar graph that displays future windows and linux partitions
`Left( `Label( _("After Installation") ) ),
`PartitionSplitter( `id(`linux_size),
win_used, win_free,
linux_size, linux_min, min_win_free,
bargraph_label_win_used,
bargraph_label_win_free,
bargraph_label_linux,
field_label_win_free,
field_label_linux
),
`VStretch()
);
// help text, continued - graphical mode only
// this help text will be appended to the help text common to both modes.
helptext = helptext + _("
<p>
The upper bar graph displays the current situation.
The lower bar graph displays the situation after the installation (after
the partition resize).
</p>
");
// help text (graphical mode), continued
helptext = helptext + _("
<p>
Drag the slider or enter a numeric value in either
input field to adjust the suggested value.
</p>");
// help text (graphical mode), continued
helptext = helptext + _("
<p>
Within the space you reserve for Linux, partitions will automatically be
created as necessary.
</p>");
}
else // no special widgets -> simple fallback UI
{
contents = `HVSquash(
`VBox(
`HBox(
// Label for used part of the Windows partition in non-graphical mode
`HWeight(3, `Right(`Label(_("Windows Used")))),
`HWeight(2, `Label(`opt(`outputField), sformat("%1", win_used ))),
`HWeight(3, `Left(`Label( unit )))
),
`VSpacing(0.5),
`HBox(
// Label for free part of the Windows partition in non-graphical mode
`HWeight(3, `Right(`Label(_("Free")))),
`HWeight(2, `Label(`opt(`outputField), sformat("%1", win_free ))),
`HWeight(3, `Left(`Label( unit )))
),
`VSpacing(0.5),
`HBox(
// Edit field label for linux partition size in non-graphical mode
`HWeight(3, `Right(`Bottom(`Label(_("Linux"))))),
`HWeight(2, `IntField( `id(`linux_size),
"", // label (above)
linux_min, // min
win_free - min_win_free, // max
linux_size // initial
)
),
`HWeight(3, `Left(`Bottom(`Label( unit ))))
)
)
);
// help text, continued - non-graphical mode only
// this help text will be appended to the help text common to both modes.
helptext = helptext + _("
<p>Enter a value for the size of your <b>Linux</b> installation.
The partitions will automatically be created within this range
as required for &product;.
</p>
");
// help text (non-graphical mode), continued
helptext = helptext + _("
<p>
<b>Windows Used</b> is the size of the used part of your Windows partition.
</p>");
// help text (non-graphical mode), continued
helptext = helptext + _("
<p><b>Free</b> indicates the current free space (before the Linux
installation) on the partition.
</p>");
}
Wizard::SetContents( // Set the dialog header
_("Resizing the Windows Partition"),
contents, helptext, (boolean)WFM::Args(0),
(boolean)WFM::Args(1) );
/////////////////////////////////////////////////////////////////////////
// END: GUI
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// START: Main loop
/////////////////////////////////////////////////////////////////////////
repeat {
ret = (symbol)Wizard::UserInput();
if( ret == `abort && Popup::ReallyAbort(true) )
return `abort;
if( ret == `next )
{
// Get the value the user adjusted. If s/he entered a value
// too big or too small this is automatically adjusted to the
// biggest/smallest value possible (by Qt).
//
linux_size = (integer)UI::QueryWidget(`id(`linux_size), `Value);
new_win_size = win_used + win_free - linux_size;
y2milestone( "Linux size: <%1> - New Win size: <%2>",
linux_size, new_win_size);
}
if( ret == `back )
{
// reset resize flag
Storage::SetDoResize( "NO" );
return `back;
}
} until ( ret == `next || ret == `back || ret == `cancel );
Storage::SaveExitKey( ret );
/////////////////////////////////////////////////////////////////////////
// END: Main loop
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// START: Final action
/////////////////////////////////////////////////////////////////////////
// Now update the target map to the new situation
//
if( !test_mode )
{
// adjust the partition entry in the target map to reflect the new size
// add flag and new size to the windows partition
//
win_partition = add( win_partition, "resize", true );
// adjust the region list in the windows partition to reflect the new size
//
integer win_start = region[0]:0; // same as before resize
integer new_length_i = PartedSizeToCly( (tofloat(new_win_size) * 1024.0 * 1024.0), cyl_size );
region = [win_start, new_length_i];
win_partition = add( win_partition, "region", region);
y2milestone( "New region of Windows partition after resize: <%1>", region );
// Insert the altered windows partition into the new cleaned partition list.
//
partitions_new = add( partitions_new, win_partition );
y2milestone( "New partition list with altered windows partition: <%1>",
partitions_new );
// now let the automatic partitioner do its work
boolean ok = create_partitions( targets, target, partitions_new );
if( !ok )
{
Popup::Message( _("The available space is not sufficient for an installation.") );
ret = `cancel;
}
}
/////////////////////////////////////////////////////////////////////////
// END: final action
/////////////////////////////////////////////////////////////////////////
return ret;
}
ACC SHELL 2018