parted.c revision 7e7bd3dccbfe8f79e25e5c1554b5bc3a9aaca321
/*
parted - a frontend to libparted
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007
Free Software Foundation, Inc.
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "closeout.h"
#include "configmake.h"
#include "version-etc.h"
#include "command.h"
#include "ui.h"
#include "table.h"
#define AUTHORS \
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "parted"
#if ENABLE_NLS
# include <libintl.h>
# include <locale.h>
#else
#endif /* ENABLE_NLS */
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "xalloc.h"
#ifdef ENABLE_MTRACE
#include <mcheck.h>
#endif
#include <getopt.h>
/* minimum amount of free space to leave, or maximum amount to gobble up */
{
}
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
};
typedef struct {
} TimerContext;
/* name, has-arg, string-return-val, char-return-val */
};
static char* options_help [][2] = {
};
char *program_name;
int opt_script_mode = 0;
int pretend_input_tty = 0;
int opt_machine_mode = 0;
int disk_is_modified = 0;
int is_toggle_mode = 0;
static char* number_msg = N_(
"NUMBER is the partition number used by Linux. On MS-DOS disk labels, the "
"primary partitions number from 1 to 4, logical partitions from 5 onwards.\n");
"extended\n");
"4GB or 10%. Negative values count from the end of the disk. "
"For example, -1s specifies exactly the last sector.\n");
"following FS-TYPEs: ");
static char* copyright_msg = N_(
"Copyright (C) 1998 - 2006 Free Software Foundation, Inc.\n"
"This program is free software, covered by the GNU General Public License.\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public License for more details.\n\n");
static char* label_type_msg;
static char* flag_msg;
static char* unit_msg;
static char* mkfs_fs_type_msg;
static char* mkpart_fs_type_msg;
static char* resize_fs_type_msg;
static TimerContext timer_context;
static int _print_list ();
static void
{
int draw_this_time;
return;
draw_this_time = 1;
} else {
draw_this_time = 0;
}
if (draw_this_time) {
wipe_line ();
if (timer->state_name)
printf (_("%0.f%%\t(time left %.2d:%.2d)"),
}
}
static int
{
char* path;
if (ped_partition_is_busy (part)) {
_("Partition %s is being used. You must unmount it "
"before you modify it with Parted."),
path);
return 0;
}
return 1;
}
static int
{
return ped_exception_throw (
_("Partition(s) on %s are being used."),
return 1;
}
static int
{
return ped_exception_throw (
_("The existing file system will be destroyed and "
"all data on the partition will be lost. Do "
"you want to continue?"),
NULL) == PED_EXCEPTION_YES;
}
static int
{
return ped_exception_throw (
_("The existing disk label on %s will be destroyed "
"and all data on this disk will be lost. Do you "
"want to continue?"),
}
/* This function changes "sector" to "new_sector" if the new value lies
* within the required range.
*/
static int
{
return 0;
*sector = new_sector;
return 1;
}
typedef enum {
MOVE_NO = 0,
MOVE_STILL = 1,
MOVE_UP = 2,
MOVE_DOWN = 4
} EMoves;
enum { /* Don't change these values */
SECT_START = 0,
SECT_END = -1
};
/* Find the prefered way to adjust the sector s inside range.
* If a move isn't allowed or is out of range it can't be selected.
* what contains SECT_START if the sector to adjust is a start sector
* or SECT_END if it's an end one.
* The prefered move is to the nearest allowed boundary of the part
* partition (if at equal distance: to start if SECT_START or to end
* if SECT_END).
* The distance is returned in dist.
*/
static EMoves
{
*dist = 0;
return MOVE_STILL;
}
else
}
else
}
move = MOVE_STILL;
&& what == SECT_START) )
else
PED_ASSERT (0, return 0);
0 ) );
return move;
}
/* Snaps a partition to nearby partition boundaries. This is useful for
* gobbling up small amounts of free space, and also for reinterpreting small
* changes to a partition as non-changes (eg: perhaps the user only wanted to
* resize the end of a partition).
* Note that this isn't the end of the story... this function is
* always called before the constraint solver kicks in. So you don't need to
* worry too much about inadvertantly creating overlapping partitions, etc.
*/
static void
{
int adjacent;
/* If we can snap to old_geom, then we will... */
/* and this will enforce the snapped positions */
if (old_geom) {
}
/* If start and end are on the same partition, we */
/* don't allow them to cross. */
if (start_part == end_part) {
start_allow &= ~MOVE_UP;
}
/* Let's find our way */
start_part, &start_dist );
/* If start and end are on adjacent partitions, */
/* and if they would prefer crossing, then refrain */
/* the farthest to do so. */
if (end_dist < start_dist) {
start_allow &= ~MOVE_UP;
start_part, &start_dist );
PED_ASSERT (start_dist >= 0, return);
} else {
PED_ASSERT (end_dist >= 0, return);
}
}
/* New positions */
start ) );
end ) );
}
/* This functions constructs a constraint from the following information:
* start, is_start_exact, end, is_end_exact.
*
* If is_start_exact == 1, then the constraint requires start be as given in
* "start". Otherwise, the constraint does not set any requirements on the
* start.
*/
static PedConstraint*
{
}
static PedConstraint*
{
return result;
}
void
{
if (!cmd) return;
}
static int
{
if (!disk)
goto error;
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
if (!ped_disk_check (disk))
goto error_destroy_disk;
if (!fs)
goto error_destroy_disk;
goto error_close_fs;
return 1;
return 0;
}
static int
{
if (!dst_disk)
goto error;
if (!command_line_is_integer ()) {
goto error_destroy_disk;
}
if (!command_line_get_partition (_("Source partition number?"),
goto error_destroy_disk;
_("Can't copy an extended partition."));
goto error_destroy_disk;
}
if (!_partition_warn_busy (src))
goto error_destroy_disk;
if (!command_line_get_partition (_("Destination partition number?"),
goto error_destroy_disk;
if (!_partition_warn_busy (dst))
goto error_destroy_disk;
/* do the copy */
if (!src_fs)
goto error_destroy_disk;
if (!dst_fs)
goto error_close_src_fs;
/* update the partition table, close disks */
goto error_destroy_disk;
if (!ped_disk_commit (dst_disk))
goto error_destroy_disk;
disk_is_modified = 1;
return 1;
return 0;
}
void
{
int i;
for (i=0; commands [i]; i++)
command_print_summary (commands [i]);
}
void
{
int i;
for (i=0; options_help [i][0]; i++) {
printf (" -%c, --%-23.23s %s\n",
options_help [i][0][0],
options_help [i][0],
_(options_help [i][1]));
}
}
int
{
if (command_line_get_word_count ()) {
char* word = command_line_pop_word ();
if (word) {
}
} else {
}
return 1;
}
static int
{
if (!disk) ped_exception_catch ();
if (disk) {
if (!_disk_warn_busy (disk))
goto error_destroy_disk;
goto error_destroy_disk;
}
goto error;
if (!disk)
goto error;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
disk_is_modified = 1;
return 1;
return 0;
}
static int
{
if (!disk)
goto error;
if (!opt_script_mode && !_partition_warn_loss())
goto error_destroy_disk;
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
goto error_destroy_disk;
if (!fs)
goto error_destroy_disk;
goto error_destroy_disk;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
disk_is_modified = 1;
return 1;
return 0;
}
static int
{
char* peek_word;
if (!disk)
goto error;
} else {
if (!command_line_get_part_type (_("Partition type?"),
goto error_destroy_disk;
}
if (part_type == PED_PARTITION_EXTENDED
} else {
if (!command_line_get_fs_type (_("File system type?"),
&fs_type))
goto error_destroy_disk;
}
if (peek_word)
goto error_destroy_disk;
goto error_destroy_disk;
/* processing starts here */
if (!part)
goto error_destroy_disk;
/* create constraints */
if (!final_constraint)
/* subject to partition constraint */
ped_constraint_any (*dev))) {
switch (ped_exception_throw (
_("You requested a partition from %s to %s.\n"
"The closest location we can manage is "
"%s to %s. "
"Is this still acceptable to you?"),
{
case PED_EXCEPTION_YES:
/* all is well in this state */
break;
case PED_EXCEPTION_NO:
case PED_EXCEPTION_UNHANDLED:
default:
/* undo partition addition */
goto error_remove_part;
}
} else {
goto error_remove_part;
}
} else {
}
/* set minor attributes */
if (part_name)
goto error_destroy_disk;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
/* clean up */
if (range_start != NULL)
disk_is_modified = 1;
return 1;
if (range_start != NULL)
return 0;
}
static int
{
if (!disk)
goto error;
} else {
if (!command_line_get_part_type (_("Partition type?"),
goto error_destroy_disk;
}
if (part_type == PED_PARTITION_EXTENDED) {
_("An extended partition cannot hold a file system. "
"Did you want mkpart?"));
goto error_destroy_disk;
}
goto error_destroy_disk;
&range_start))
goto error_destroy_disk;
goto error_destroy_disk;
/* attempt to create the partition now */
if (!part)
goto error_destroy_disk;
/* create constraints */
if (!final_constraint)
/* subject to partition constraint */
ped_constraint_any (*dev))) {
switch (ped_exception_throw (
_("You requested a partition from %s to %s.\n"
"The closest location we can manage is "
"%s to %s. "
"Is this still acceptable to you?"),
case PED_EXCEPTION_YES:
/* all is well in this state */
break;
case PED_EXCEPTION_NO:
case PED_EXCEPTION_UNHANDLED:
default:
/* undo partition addition */
goto error_remove_part;
}
} else {
goto error_remove_part;
}
} else {
}
/* set LBA flag automatically if available */
/* fs creation */
if (!fs)
goto error_destroy_disk;
goto error_destroy_disk;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
/* clean up */
if (range_start != NULL)
disk_is_modified = 1;
return 1;
if (range_start != NULL)
return 0;
}
static int
{
if (!disk)
goto error;
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
_("Can't move an extended partition."));
goto error_destroy_disk;
}
if (!fs)
goto error_destroy_disk;
/* get new target */
goto error_close_fs;
goto error_close_fs;
/* set / test on "disk" */
goto error_close_fs;
goto error_destroy_constraint;
_("Can't move a partition onto itself. Try using "
"resize, perhaps?"));
goto error_close_fs;
}
/* do the move */
if (!fs_copy)
goto error_close_fs;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
if (range_start != NULL)
disk_is_modified = 1;
return 1;
if (range_start != NULL)
return 0;
}
static int
{
char* name;
if (!disk)
goto error;
goto error_destroy_disk;
if (!name)
goto error_destroy_disk;
goto error_free_name;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
return 1;
return 0;
}
static char*
{
int first_flag;
const char* name;
*res = '\0';
first_flag = 1;
if (first_flag)
first_flag = 0;
else {
+ 1 + 2);
}
}
}
return res;
}
/* Prints a sector out, first in compact form, and then with a percentage.
* Eg: 32Gb (40%)
*/
static void
{
char* compact;
char* percent;
if (ped_unit_get_default() == PED_UNIT_PERCENT)
else
}
static int
{
char* flags;
if (!fs)
return 1;
putchar ('\n');
if (resize_constraint) {
}
putchar ('\n');
return 1;
}
static int
{
int has_extended;
int has_name;
int has_devices_arg = 0;
int has_free_arg = 0;
int has_list_arg = 0;
int has_num_arg = 0;
"cpqarray", "file", "ataraid", "i2o",
"ubd", "dasd", "viodasd", "sx8", "dm"};
char* peek_word;
char* start;
char* end;
char* size;
const char* name;
char* tmp;
if (!disk)
goto error;
if (peek_word) {
has_devices_arg = 1;
}
has_free_arg = 1;
}
has_list_arg = 1;
}
else
}
if (has_devices_arg) {
char* dev_name;
* current_dev->sector_size);
}
if (!*dev)
return 0;
if (!ped_device_open (*dev))
return 0;
return 1;
}
else if (has_list_arg)
return _print_list ();
else if (has_num_arg) {
int status = 0;
return status;
}
- (default_unit == PED_UNIT_CHS ||
if (opt_machine_mode) {
switch (default_unit) {
break;
break;
default: puts ("BYT;");
break;
}
printf ("%s:%s:%s:%lld:%lld:%s:%s;\n",
} else {
printf (_("Model: %s (%s)\n"),
}
if (ped_unit_get_default () == PED_UNIT_CHS
|| ped_unit_get_default () == PED_UNIT_CYLINDER) {
if (opt_machine_mode) {
printf ("%d:%d:%d:%s;\n",
} else {
printf (_("BIOS cylinder,head,sector geometry: %d,%d,%d. "
"Each cylinder is %s.\n"),
}
}
if (!opt_machine_mode) {
putchar ('\n');
}
if (!opt_machine_mode) {
if (ped_unit_get_default() == PED_UNIT_CHS) {
_("End"), NULL);
} else {
}
if (has_extended)
if (has_name)
continue;
else
*dev,
if (ped_unit_get_default() == PED_UNIT_CHS) {
} else {
}
if (has_extended) {
}
if (has_name) {
}
} else {
if (has_extended)
if (has_name)
}
}
#ifdef ENABLE_NLS
#else
#endif
} else {
continue;
else
*dev,
if (ped_unit_get_default() != PED_UNIT_CHS)
else
putchar (':');
if (has_name)
else
putchar (':');
} else {
puts ("free;");
}
}
}
return 1;
return 0;
}
static int
_print_list ()
{
do_print (¤t_dev);
putchar ('\n');
}
return 1;
}
static int
{
exit (0);
}
static PedPartitionType
{
if (!extended
return 0;
return PED_PARTITION_LOGICAL;
}
/* This function checks if "part" contains a file system, and returs
* 0 if either no file system was found, or the user declined to add it.
* 1 if a file system was found, and the user chose to add it.
* -1 if the user chose to cancel the entire search.
*/
static int
{
const PedFileSystemType* fs_type;
char* found_start;
char* found_end;
if (!fs_type)
return 0;
if (!probed)
return 0;
return 0;
}
return 0;
}
_("A %s %s partition was found at %s -> %s. "
"Do you want to add it to the partition table?"),
switch (ex_opt) {
case PED_EXCEPTION_CANCEL: return -1;
case PED_EXCEPTION_NO: return 0;
default: break;
}
return 1;
}
/* hack: we only iterate through the start, since most (all) fs's have their
* superblocks at the start. We'll need to change this if we generalize
* for RAID, or something...
*/
static int
{
/ start_range->length);
if (!part) {
continue;
}
switch (_rescue_add_partition (part)) {
case 1:
return 1;
case 0:
break;
case -1:
goto error_remove_partition;
}
} else {
}
}
return 1;
return 0;
}
static int
{
if (!disk)
goto error;
goto error_destroy_disk;
goto error_destroy_disk;
goto error_destroy_disk;
disk_is_modified = 1;
return 1;
return 0;
}
static int
{
if (!disk)
goto error;
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
}
goto error_destroy_disk;
goto error_destroy_disk;
goto error_destroy_disk;
goto error_destroy_constraint;
} else {
if (!fs)
goto error_destroy_disk;
goto error_close_fs;
goto error_close_fs;
/* may have changed... eg fat16 -> fat32 */
}
if (range_start != NULL)
disk_is_modified = 1;
return 1;
if (range_start != NULL)
return 0;
}
static int
{
if (!disk)
goto error;
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
disk_is_modified = 1;
return 1;
return 0;
}
static int
{
return 0;
if (!ped_device_open (new_dev))
return 0;
ped_device_close (*dev);
print_using_dev (*dev);
return 1;
}
static int
{
int state;
if (!disk)
goto error;
goto error_destroy_disk;
goto error_destroy_disk;
if (!is_toggle_mode) {
goto error_destroy_disk;
}
goto error_destroy_disk;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
disk_is_modified = 1;
return 1;
return 0;
}
static int
{
int result;
is_toggle_mode = 1;
is_toggle_mode = 0;
return result;
}
static int
{
return 0;
return 1;
}
static int
do_version ()
{
printf ("\n%s\n%s",
_(copyright_msg));
return 1;
}
static void
{
int first;
/* flags */
first = 1;
if (first)
first = 0;
else
}
/* units */
first = 1;
if (first)
first = 0;
else
}
/* disk type */
first = 1;
continue;
if (first)
first = 0;
else
}
/* mkfs - file system types */
first = 1;
continue;
if (first)
first = 0;
else
}
/* mkpart - file system types */
first = 1;
if (first)
first = 0;
else
}
/* resize - file system types */
first = 1;
continue;
if (first)
first = 0;
else
}
}
static void
{
}
static void
{
_("check NUMBER do a simple check on the file "
"system"),
NULL),
_("cp [FROM-DEVICE] FROM-NUMBER TO-NUMBER copy file system to another "
"partition"),
NULL),
_("help [COMMAND] print general help, or help "
"on COMMAND"),
NULL),
NULL, 1));
_("mklabel,mktable LABEL-TYPE create a new disklabel "
"(partition table)"),
NULL),
_("mkfs NUMBER FS-TYPE make a FS-TYPE file "
"system on partititon NUMBER"),
NULL),
_("mkpart PART-TYPE [FS-TYPE] START END make a partition"),
NULL),
_(start_end_msg),
"\n",
_("'mkpart' makes a partition without creating a new file system on the "
"partition. FS-TYPE may be specified to set an appropriate partition ID.\n"),
NULL), 1));
_("mkpartfs PART-TYPE FS-TYPE START END make a partition with a "
"file system"),
NULL),
_("move NUMBER START END move partition NUMBER"),
NULL),
_("name NUMBER NAME name partition NUMBER as NAME"),
NULL),
_("print [devices|free|list,all|NUMBER] display the partition table, "
"available devices, free space, all found partitions, or a particular "
"partition"),
NULL),
_("Without arguments, 'print' displays the entire partition table. However "
"with the following arguments it performs various other actions.\n"),
_(" devices : display all active block devices\n"),
_(" free : display information about free unpartitioned space on the "
"current block device\n"),
_(" list, all : display the partition tables of all active block devices\n"),
_(" NUMBER : display more detailed information about this particular "
"partition\n"),
NULL), 1));
_("quit exit program"),
NULL),
NULL, 1));
_("rescue START END rescue a lost partition near "
"START and END"),
NULL),
_("resize NUMBER START END resize partition NUMBER and "
"its file system"),
NULL),
_(start_end_msg),
_("rm NUMBER delete partition NUMBER"),
NULL),
_("select DEVICE choose the device to edit"),
NULL),
_("set NUMBER FLAG STATE change the FLAG on partition "
"NUMBER"),
NULL),
_("toggle [NUMBER [FLAG]] toggle the state of FLAG on "
"partition NUMBER"),
NULL),
_("unit UNIT set the default unit to UNIT"),
NULL),
_("version display the version number "
"and copyright information of GNU Parted"),
NULL),
_("'version' displays copyright and version information corresponding to this "
"copy of GNU Parted\n"),
NULL), 1));
}
static void
{
command_destroy (*walk);
}
}
static void
_init_i18n ()
{
/* intialize i18n */
#ifdef ENABLE_NLS
#endif /* ENABLE_NLS */
}
void
_version ()
{
(char *) NULL);
}
static int
{
while (1)
{
if (opt == -1)
break;
switch (opt) {
case PRETEND_INPUT_TTY:
pretend_input_tty = 1;
break;
default: wrong = 1; break;
}
}
if (wrong == 1) {
_("Usage: %s [-hlmsv] [DEVICE [COMMAND [PARAMETERS]]...]\n"),
return 0;
}
if (version == 1) {
_version ();
exit (EXIT_SUCCESS);
}
if (help == 1) {
help_msg ();
exit (EXIT_SUCCESS);
}
if (list == 1) {
_print_list ();
exit (EXIT_SUCCESS);
}
return 1;
}
static PedDevice*
{
/* specified on comand line? */
if (*argc_ptr) {
if (!dev)
return NULL;
(*argc_ptr)--;
(*argv_ptr)++;
} else {
if (!dev) {
_("No device found"))
goto retry;
else
return NULL;
}
}
if (!ped_device_open (dev))
return NULL;
return dev;
}
static PedDevice*
{
#ifdef ENABLE_MTRACE
mtrace();
#endif
_init_i18n ();
if (!init_ui ())
goto error;
_init_messages ();
_init_commands ();
goto error_done_commands;
#ifdef HAVE_GETUID
if (getuid() != 0 && !opt_script_mode) {
puts (_("WARNING: You are not superuser. Watch out for "
"permissions."));
}
#endif
if (!dev)
goto error_done_commands;
if (!g_timer)
goto error_done_commands;
timer_context.last_update = 0;
return dev;
_done_commands ();
_done_messages ();
done_ui ();
return NULL;
}
static void
{
_("You should reinstall your boot loader before "
"rebooting. Read section 4 of the Parted User "
"documentation for more information."));
}
#if !defined(__sun)
}
#endif
_done_commands ();
_done_messages ();
done_ui();
}
int
{
int status;
program_name = argv[0];
if (!dev)
return 1;
if (argc || opt_script_mode)
else
return !status;
}