blob: d16ad96f6b251d4f0f7619058a6df6320a341cbf [file] [log] [blame]
/*
parted - a frontend to libparted
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <config.h>
#include "closeout.h"
#include "configmake.h"
#include "version-etc.h"
#include "command.h"
#include "ui.h"
#include "table.h"
#define AUTHORS \
"<http://parted.alioth.debian.org/cgi-bin/trac.cgi/browser/AUTHORS>"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "parted"
#define N_(String) String
#if ENABLE_NLS
# include <libintl.h>
# include <locale.h>
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#include <parted/parted.h>
#include <parted/debug.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#ifdef ENABLE_MTRACE
#include <mcheck.h>
#endif
#include <getopt.h>
/* minimum amount of free space to leave, or maximum amount to gobble up */
#define MIN_FREESPACE (1000 * 2) /* 1000k */
static int MEGABYTE_SECTORS (PedDevice* dev)
{
return PED_MEGABYTE_SIZE / dev->sector_size;
}
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
{
PRETEND_INPUT_TTY = CHAR_MAX + 1,
};
typedef struct {
time_t last_update;
time_t predicted_time_left;
} TimerContext;
static struct option options[] = {
/* name, has-arg, string-return-val, char-return-val */
{"help", 0, NULL, 'h'},
{"list", 0, NULL, 'l'},
{"machine", 0, NULL, 'm'},
{"script", 0, NULL, 's'},
{"version", 0, NULL, 'v'},
{"-pretend-input-tty", 0, NULL, PRETEND_INPUT_TTY},
{NULL, 0, NULL, 0}
};
static char* options_help [][2] = {
{"help", N_("displays this help message")},
{"list", N_("lists partition layout on all block devices")},
{"machine", N_("displays machine parseable output")},
{"script", N_("never prompts for user intervention")},
{"version", N_("displays the version")},
{NULL, NULL}
};
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");
static char* label_type_msg_start = N_("LABEL-TYPE is one of: ");
static char* flag_msg_start = N_("FLAG is one of: ");
static char* unit_msg_start = N_("UNIT is one of: ");
static char* part_type_msg = N_("PART-TYPE is one of: primary, logical, "
"extended\n");
static char* fs_type_msg_start = N_("FS-TYPE is one of: ");
static char* start_end_msg = N_("START and END are disk locations, such as "
"4GB or 10%. Negative values count from the end of the disk. "
"For example, -1s specifies exactly the last sector.\n");
static char* state_msg = N_("STATE is one of: on, off\n");
static char* device_msg = N_("DEVICE is usually /dev/hda or /dev/sda\n");
static char* name_msg = N_("NAME is any word you want\n");
static char* resize_msg_start = N_("The partition must have one of the "
"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 Command* commands [256] = {NULL};
static PedTimer* g_timer;
static TimerContext timer_context;
static int _print_list ();
static void _done (PedDevice* dev);
static void
_timer_handler (PedTimer* timer, void* context)
{
TimerContext* tcontext = (TimerContext*) context;
int draw_this_time;
if (opt_script_mode || !isatty(fileno(stdout)))
return;
if (tcontext->last_update != timer->now && timer->now > timer->start) {
tcontext->predicted_time_left
= timer->predicted_end - timer->now;
tcontext->last_update = timer->now;
draw_this_time = 1;
} else {
draw_this_time = 0;
}
if (draw_this_time) {
wipe_line ();
if (timer->state_name)
printf ("%s... ", timer->state_name);
printf (_("%0.f%%\t(time left %.2d:%.2d)"),
100.0 * timer->frac,
(int) (tcontext->predicted_time_left / 60),
(int) (tcontext->predicted_time_left % 60));
fflush (stdout);
}
}
static int
_partition_warn_busy (PedPartition* part)
{
char* path;
if (ped_partition_is_busy (part)) {
path = ped_partition_get_path (part);
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Partition %s is being used. You must unmount it "
"before you modify it with Parted."),
path);
ped_free (path);
return 0;
}
return 1;
}
static int
_disk_warn_busy (PedDisk* disk)
{
if (ped_device_is_busy (disk->dev))
return ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_IGNORE_CANCEL,
_("Partition(s) on %s are being used."),
disk->dev->path) == PED_EXCEPTION_IGNORE;
return 1;
}
static int
_partition_warn_loss ()
{
return ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_YES_NO,
_("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
_disk_warn_loss (PedDisk* disk)
{
return ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_YES_NO,
_("The existing disk label on %s will be destroyed "
"and all data on this disk will be lost. Do you "
"want to continue?"),
disk->dev->path) == PED_EXCEPTION_YES;
}
/* This function changes "sector" to "new_sector" if the new value lies
* within the required range.
*/
static int
snap (PedSector* sector, PedSector new_sector, PedGeometry* range)
{
PED_ASSERT (ped_geometry_test_sector_inside (range, *sector), return 0);
if (!ped_geometry_test_sector_inside (range, new_sector))
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
prefer_snap (PedSector s, int what, PedGeometry* range, EMoves* allow,
PedPartition* part, PedSector* dist)
{
PedSector up_dist = -1, down_dist = -1;
PedSector new_sect;
EMoves move;
PED_ASSERT (what == SECT_START || what == SECT_END, return 0);
if (!(*allow & (MOVE_UP | MOVE_DOWN))) {
*dist = 0;
return MOVE_STILL;
}
if (*allow & MOVE_UP) {
new_sect = part->geom.end + 1 + what;
if (ped_geometry_test_sector_inside (range, new_sect))
up_dist = new_sect - s;
else
*allow &= ~MOVE_UP;
}
if (*allow & MOVE_DOWN) {
new_sect = part->geom.start + what;
if (ped_geometry_test_sector_inside (range, new_sect))
down_dist = s - new_sect;
else
*allow &= ~MOVE_DOWN;
}
move = MOVE_STILL;
if ((*allow & MOVE_UP) && (*allow & MOVE_DOWN)) {
if (down_dist < up_dist || (down_dist == up_dist
&& what == SECT_START) )
move = MOVE_DOWN;
else if (up_dist < down_dist || (down_dist == up_dist
&& what == SECT_END) )
move = MOVE_UP;
else
PED_ASSERT (0, return 0);
} else if (*allow & MOVE_UP)
move = MOVE_UP;
else if (*allow & MOVE_DOWN)
move = MOVE_DOWN;
*dist = ( move == MOVE_DOWN ? down_dist :
( move == MOVE_UP ? up_dist :
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
snap_to_boundaries (PedGeometry* new_geom, PedGeometry* old_geom,
PedDisk* disk,
PedGeometry* start_range, PedGeometry* end_range)
{
PedPartition* start_part;
PedPartition* end_part;
PedSector start = new_geom->start;
PedSector end = new_geom->end;
PedSector start_dist = -1, end_dist = -1;
EMoves start_allow, end_allow, start_want, end_want;
int adjacent;
start_want = end_want = MOVE_NO;
start_allow = end_allow = MOVE_STILL | MOVE_UP | MOVE_DOWN;
start_part = ped_disk_get_partition_by_sector (disk, start);
end_part = ped_disk_get_partition_by_sector (disk, end);
adjacent = (start_part->geom.end + 1 == end_part->geom.start);
/* If we can snap to old_geom, then we will... */
/* and this will enforce the snapped positions */
if (old_geom) {
if (snap (&start, old_geom->start, start_range))
start_allow = MOVE_STILL;
if (snap (&end, old_geom->end, end_range))
end_allow = MOVE_STILL;
}
/* 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;
end_allow &= ~MOVE_DOWN;
}
/* Let's find our way */
start_want = prefer_snap (start, SECT_START, start_range, &start_allow,
start_part, &start_dist );
end_want = prefer_snap (end, SECT_END, end_range, &end_allow,
end_part, &end_dist );
PED_ASSERT (start_dist >= 0 && end_dist >= 0, return);
/* If start and end are on adjacent partitions, */
/* and if they would prefer crossing, then refrain */
/* the farthest to do so. */
if (adjacent && start_want == MOVE_UP && end_want == MOVE_DOWN) {
if (end_dist < start_dist) {
start_allow &= ~MOVE_UP;
start_want = prefer_snap (start, SECT_START,
start_range, &start_allow,
start_part, &start_dist );
PED_ASSERT (start_dist >= 0, return);
} else {
end_allow &= ~MOVE_DOWN;
end_want = prefer_snap (end, SECT_END,
end_range, &end_allow,
end_part, &end_dist );
PED_ASSERT (end_dist >= 0, return);
}
}
/* New positions */
start = ( start_want == MOVE_DOWN ? start_part->geom.start :
( start_want == MOVE_UP ? start_part->geom.end + 1 :
start ) );
end = ( end_want == MOVE_DOWN ? end_part->geom.start - 1 :
( end_want == MOVE_UP ? end_part->geom.end :
end ) );
PED_ASSERT (ped_geometry_test_sector_inside(start_range,start), return);
PED_ASSERT (ped_geometry_test_sector_inside (end_range, end), return);
PED_ASSERT (start <= end,
PED_DEBUG (0, "start = %d, end = %d\n", start, end));
ped_geometry_set (new_geom, start, end - start + 1);
}
/* 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*
constraint_from_start_end (PedDevice* dev, PedGeometry* range_start,
PedGeometry* range_end)
{
return ped_constraint_new (ped_alignment_any, ped_alignment_any,
range_start, range_end, 1, dev->length);
}
static PedConstraint*
constraint_intersect_and_destroy (PedConstraint* a, PedConstraint* b)
{
PedConstraint* result = ped_constraint_intersect (a, b);
ped_constraint_destroy (a);
ped_constraint_destroy (b);
return result;
}
void
help_on (char* topic)
{
Command* cmd;
cmd = command_get (commands, topic);
if (!cmd) return;
command_print_help (cmd);
}
static int
do_check (PedDevice** dev)
{
PedDisk* disk;
PedFileSystem* fs;
PedPartition* part = NULL;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!command_line_get_partition (_("Partition number?"), disk, &part))
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
if (!ped_disk_check (disk))
goto error_destroy_disk;
fs = ped_file_system_open (&part->geom);
if (!fs)
goto error_destroy_disk;
if (!ped_file_system_check (fs, g_timer))
goto error_close_fs;
ped_file_system_close (fs);
ped_disk_destroy (disk);
return 1;
error_close_fs:
ped_file_system_close (fs);
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
static int
do_cp (PedDevice** dev)
{
PedDisk* src_disk;
PedDisk* dst_disk;
PedPartition* src = NULL;
PedPartition* dst = NULL;
PedFileSystem* src_fs;
PedFileSystem* dst_fs;
PedFileSystemType* dst_fs_type;
dst_disk = ped_disk_new (*dev);
if (!dst_disk)
goto error;
src_disk = dst_disk;
if (!command_line_is_integer ()) {
if (!command_line_get_disk (_("Source device?"), &src_disk))
goto error_destroy_disk;
}
if (!command_line_get_partition (_("Source partition number?"),
src_disk, &src))
goto error_destroy_disk;
if (src->type == PED_PARTITION_EXTENDED) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("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?"),
dst_disk, &dst))
goto error_destroy_disk;
if (!_partition_warn_busy (dst))
goto error_destroy_disk;
/* do the copy */
src_fs = ped_file_system_open (&src->geom);
if (!src_fs)
goto error_destroy_disk;
dst_fs = ped_file_system_copy (src_fs, &dst->geom, g_timer);
if (!dst_fs)
goto error_close_src_fs;
dst_fs_type = dst_fs->type; /* may be different to src_fs->type */
ped_file_system_close (src_fs);
ped_file_system_close (dst_fs);
/* update the partition table, close disks */
if (!ped_partition_set_system (dst, dst_fs_type))
goto error_destroy_disk;
if (!ped_disk_commit (dst_disk))
goto error_destroy_disk;
if (src_disk != dst_disk)
ped_disk_destroy (src_disk);
ped_disk_destroy (dst_disk);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_close_src_fs:
ped_file_system_close (src_fs);
error_destroy_disk:
if (src_disk && src_disk != dst_disk)
ped_disk_destroy (src_disk);
ped_disk_destroy (dst_disk);
error:
return 0;
}
void
print_commands_help ()
{
int i;
for (i=0; commands [i]; i++)
command_print_summary (commands [i]);
}
void
print_options_help ()
{
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
do_help (PedDevice** dev)
{
if (command_line_get_word_count ()) {
char* word = command_line_pop_word ();
if (word) {
help_on (word);
free (word);
}
} else {
print_commands_help();
}
return 1;
}
static int
do_mklabel (PedDevice** dev)
{
PedDisk* disk;
const PedDiskType* type = ped_disk_probe (*dev);
ped_exception_fetch_all ();
disk = ped_disk_new (*dev);
if (!disk) ped_exception_catch ();
ped_exception_leave_all ();
if (disk) {
if (!opt_script_mode) {
if (!_disk_warn_busy (disk))
goto error_destroy_disk;
if (!_disk_warn_loss (disk))
goto error_destroy_disk;
}
ped_disk_destroy (disk);
}
if (!command_line_get_disk_type (_("New disk label type?"), &type))
goto error;
disk = ped_disk_new_fresh (*dev, type);
if (!disk)
goto error;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
static int
do_mkfs (PedDevice** dev)
{
PedDisk* disk;
PedPartition* part = NULL;
const PedFileSystemType* type = ped_file_system_type_get ("ext2");
PedFileSystem* fs;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!opt_script_mode && !_partition_warn_loss())
goto error_destroy_disk;
if (!command_line_get_partition (_("Partition number?"), disk, &part))
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
if (!command_line_get_fs_type (_("File system?"), &type))
goto error_destroy_disk;
fs = ped_file_system_create (&part->geom, type, g_timer);
if (!fs)
goto error_destroy_disk;
ped_file_system_close (fs);
if (!ped_partition_set_system (part, type))
goto error_destroy_disk;
if (ped_partition_is_flag_available (part, PED_PARTITION_LBA))
ped_partition_set_flag (part, PED_PARTITION_LBA, 1);
if (!ped_disk_commit (disk))
goto error_destroy_disk;
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
static int
do_mkpart (PedDevice** dev)
{
PedDisk* disk;
PedPartition* part;
PedPartitionType part_type;
const PedFileSystemType* fs_type = ped_file_system_type_get ("ext2");
PedSector start = 0, end = 0;
PedGeometry *range_start = NULL, *range_end = NULL;
PedConstraint* user_constraint;
PedConstraint* dev_constraint;
PedConstraint* final_constraint;
char* peek_word;
char* part_name = NULL;
char *start_usr = NULL, *end_usr = NULL;
char *start_sol = NULL, *end_sol = NULL;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED)) {
part_type = PED_PARTITION_NORMAL;
} else {
if (!command_line_get_part_type (_("Partition type?"),
disk, &part_type))
goto error_destroy_disk;
}
if (ped_disk_type_check_feature (disk->type,
PED_DISK_TYPE_PARTITION_NAME))
part_name = command_line_get_word (_("Partition name?"),
"", NULL, 1);
peek_word = command_line_peek_word ();
if (part_type == PED_PARTITION_EXTENDED
|| (peek_word && isdigit (peek_word[0]))) {
fs_type = NULL;
} else {
if (!command_line_get_fs_type (_("File system type?"),
&fs_type))
goto error_destroy_disk;
}
if (peek_word)
ped_free (peek_word);
if (!command_line_get_sector (_("Start?"), *dev, &start, &range_start))
goto error_destroy_disk;
if (!command_line_get_sector (_("End?"), *dev, &end, &range_end))
goto error_destroy_disk;
/* processing starts here */
part = ped_partition_new (disk, part_type, fs_type, start, end);
if (!part)
goto error_destroy_disk;
snap_to_boundaries (&part->geom, NULL, disk, range_start, range_end);
/* create constraints */
user_constraint = constraint_from_start_end (*dev, range_start,
range_end);
PED_ASSERT (user_constraint != NULL, return 0);
dev_constraint = ped_device_get_constraint (*dev);
PED_ASSERT (dev_constraint != NULL, return 0);
final_constraint = ped_constraint_intersect (user_constraint,
dev_constraint);
if (!final_constraint)
goto error_destroy_simple_constraints;
/* subject to partition constraint */
ped_exception_fetch_all();
if (!ped_disk_add_partition (disk, part, final_constraint)) {
ped_exception_leave_all();
if (ped_disk_add_partition (disk, part,
ped_constraint_any (*dev))) {
start_usr = ped_unit_format (*dev, start);
end_usr = ped_unit_format (*dev, end);
start_sol = ped_unit_format (*dev, part->geom.start);
end_sol = ped_unit_format (*dev, part->geom.end);
switch (ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_YES_NO,
_("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?"),
start_usr, end_usr, start_sol, end_sol))
{
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;
}
}
ped_exception_catch();
/* set minor attributes */
if (part_name)
PED_ASSERT (ped_partition_set_name (part, part_name), return 0);
if (!ped_partition_set_system (part, fs_type))
goto error_destroy_disk;
if (ped_partition_is_flag_available (part, PED_PARTITION_LBA))
ped_partition_set_flag (part, PED_PARTITION_LBA, 1);
if (!ped_disk_commit (disk))
goto error_destroy_disk;
/* clean up */
ped_constraint_destroy (final_constraint);
ped_constraint_destroy (user_constraint);
ped_constraint_destroy (dev_constraint);
ped_disk_destroy (disk);
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
if (start_usr != NULL)
ped_free (start_usr);
if (end_usr != NULL)
ped_free (end_usr);
if (start_sol != NULL)
ped_free (start_sol);
if (end_sol != NULL)
ped_free (end_sol);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_remove_part:
ped_disk_remove_partition (disk, part);
ped_constraint_destroy (final_constraint);
error_destroy_simple_constraints:
ped_constraint_destroy (user_constraint);
ped_constraint_destroy (dev_constraint);
ped_partition_destroy (part);
error_destroy_disk:
ped_disk_destroy (disk);
error:
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
if (start_usr != NULL)
ped_free (start_usr);
if (end_usr != NULL)
ped_free (end_usr);
if (start_sol != NULL)
ped_free (start_sol);
if (end_sol != NULL)
ped_free (end_sol);
return 0;
}
static int
do_mkpartfs (PedDevice** dev)
{
PedDisk* disk;
PedPartition* part;
PedPartitionType part_type;
const PedFileSystemType* fs_type = ped_file_system_type_get ("ext2");
PedSector start = 0, end = 0;
PedGeometry *range_start = NULL, *range_end = NULL;
PedConstraint* user_constraint;
PedConstraint* dev_constraint;
PedConstraint* final_constraint;
PedFileSystem* fs;
char* part_name = NULL;
char *start_usr = NULL, *end_usr = NULL;
char *start_sol = NULL, *end_sol = NULL;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED)) {
part_type = PED_PARTITION_NORMAL;
} else {
if (!command_line_get_part_type (_("Partition type?"),
disk, &part_type))
goto error_destroy_disk;
}
if (ped_disk_type_check_feature (disk->type,
PED_DISK_TYPE_PARTITION_NAME))
part_name = command_line_get_word (_("Partition name?"),
"", NULL, 1);
if (part_type == PED_PARTITION_EXTENDED) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("An extended partition cannot hold a file system. "
"Did you want mkpart?"));
goto error_destroy_disk;
}
if (!command_line_get_fs_type (_("File system type?"), &fs_type))
goto error_destroy_disk;
if (!command_line_get_sector (_("Start?"), *dev, &start,
&range_start))
goto error_destroy_disk;
if (!command_line_get_sector (_("End?"), *dev, &end, &range_end))
goto error_destroy_disk;
/* attempt to create the partition now */
part = ped_partition_new (disk, part_type, fs_type, start, end);
if (!part)
goto error_destroy_disk;
snap_to_boundaries (&part->geom, NULL, disk, range_start, range_end);
/* create constraints */
user_constraint = constraint_from_start_end (*dev, range_start,
range_end);
PED_ASSERT (user_constraint != NULL, return 0);
dev_constraint = ped_device_get_constraint (*dev);
PED_ASSERT (dev_constraint != NULL, return 0);
final_constraint = ped_constraint_intersect (user_constraint,
dev_constraint);
if (!final_constraint)
goto error_destroy_simple_constraints;
/* subject to partition constraint */
ped_exception_fetch_all();
if (!ped_disk_add_partition (disk, part, final_constraint)) {
ped_exception_leave_all();
if (ped_disk_add_partition (disk, part,
ped_constraint_any (*dev))) {
start_usr = ped_unit_format (*dev, start);
end_usr = ped_unit_format (*dev, end);
start_sol = ped_unit_format (*dev, part->geom.start);
end_sol = ped_unit_format (*dev, part->geom.end);
switch (ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_YES_NO,
_("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?"),
start_usr, end_usr, start_sol, end_sol)) {
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;
}
}
ped_exception_catch();
ped_exception_leave_all();
/* set LBA flag automatically if available */
if (ped_partition_is_flag_available (part, PED_PARTITION_LBA))
ped_partition_set_flag (part, PED_PARTITION_LBA, 1);
/* fs creation */
fs = ped_file_system_create (&part->geom, fs_type, g_timer);
if (!fs)
goto error_destroy_disk;
ped_file_system_close (fs);
if (!ped_partition_set_system (part, fs_type))
goto error_destroy_disk;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
/* clean up */
ped_constraint_destroy (final_constraint);
ped_constraint_destroy (user_constraint);
ped_constraint_destroy (dev_constraint);
ped_disk_destroy (disk);
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
if (start_usr != NULL)
ped_free (start_usr);
if (end_usr != NULL)
ped_free (end_usr);
if (start_sol != NULL)
ped_free (start_sol);
if (end_sol != NULL)
ped_free (end_sol);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_remove_part:
ped_disk_remove_partition (disk, part);
ped_constraint_destroy (final_constraint);
error_destroy_simple_constraints:
ped_constraint_destroy (user_constraint);
ped_constraint_destroy (dev_constraint);
ped_partition_destroy (part);
error_destroy_disk:
ped_disk_destroy (disk);
error:
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
if (start_usr != NULL)
ped_free (start_usr);
if (end_usr != NULL)
ped_free (end_usr);
if (start_sol != NULL)
ped_free (start_sol);
if (end_sol != NULL)
ped_free (end_sol);
return 0;
}
static int
do_move (PedDevice** dev)
{
PedDisk* disk;
PedPartition* part = NULL;
PedFileSystem* fs;
PedFileSystem* fs_copy;
PedConstraint* constraint;
PedSector start = 0, end = 0;
PedGeometry *range_start = NULL, *range_end = NULL;
PedGeometry old_geom, new_geom;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!command_line_get_partition (_("Partition number?"), disk, &part))
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
if (part->type == PED_PARTITION_EXTENDED) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Can't move an extended partition."));
goto error_destroy_disk;
}
old_geom = part->geom;
fs = ped_file_system_open (&old_geom);
if (!fs)
goto error_destroy_disk;
/* get new target */
if (!command_line_get_sector (_("Start?"), *dev, &start, &range_start))
goto error_close_fs;
end = start + old_geom.length - 1;
if (!command_line_get_sector (_("End?"), *dev, &end, &range_end))
goto error_close_fs;
/* set / test on "disk" */
if (!ped_geometry_init (&new_geom, *dev, start, end - start + 1))
goto error_close_fs;
snap_to_boundaries (&new_geom, NULL, disk, range_start, range_end);
constraint = constraint_intersect_and_destroy (
ped_file_system_get_copy_constraint (fs, *dev),
constraint_from_start_end(*dev,range_start,range_end));
if (!ped_disk_set_partition_geom (disk, part, constraint,
new_geom.start, new_geom.end))
goto error_destroy_constraint;
ped_constraint_destroy (constraint);
if (ped_geometry_test_overlap (&old_geom, &part->geom)) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Can't move a partition onto itself. Try using "
"resize, perhaps?"));
goto error_close_fs;
}
/* do the move */
fs_copy = ped_file_system_copy (fs, &part->geom, g_timer);
if (!fs_copy)
goto error_close_fs;
ped_file_system_close (fs_copy);
ped_file_system_close (fs);
if (!ped_disk_commit (disk))
goto error_destroy_disk;
ped_disk_destroy (disk);
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_destroy_constraint:
ped_constraint_destroy (constraint);
error_close_fs:
ped_file_system_close (fs);
error_destroy_disk:
ped_disk_destroy (disk);
error:
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
return 0;
}
static int
do_name (PedDevice** dev)
{
PedDisk* disk;
PedPartition* part = NULL;
char* name;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!command_line_get_partition (_("Partition number?"), disk, &part))
goto error_destroy_disk;
name = command_line_get_word (_("Partition name?"),
ped_partition_get_name (part), NULL, 0);
if (!name)
goto error_destroy_disk;
if (!ped_partition_set_name (part, name))
goto error_free_name;
free (name);
if (!ped_disk_commit (disk))
goto error_destroy_disk;
ped_disk_destroy (disk);
return 1;
error_free_name:
free (name);
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
static char*
partition_print_flags (PedPartition* part)
{
PedPartitionFlag flag;
int first_flag;
const char* name;
char* res = ped_malloc(1);
void* _res = res;
*res = '\0';
first_flag = 1;
for (flag = ped_partition_flag_next (0); flag;
flag = ped_partition_flag_next (flag)) {
if (ped_partition_get_flag (part, flag)) {
if (first_flag)
first_flag = 0;
else {
_res = res;
ped_realloc (&_res, strlen (res)
+ 1 + 2);
res = _res;
strncat (res, ", ", 2);
}
name = _(ped_partition_flag_get_name (flag));
_res = res;
ped_realloc (&_res, strlen (res) + 1
+ strlen (name));
res = _res;
strncat (res, name, 21);
}
}
return res;
}
/* Prints a sector out, first in compact form, and then with a percentage.
* Eg: 32Gb (40%)
*/
static void
print_sector_compact_and_percent (PedSector sector, PedDevice* dev)
{
char* compact;
char* percent;
if (ped_unit_get_default() == PED_UNIT_PERCENT)
compact = ped_unit_format (dev, sector);
else
compact = ped_unit_format_custom (dev, sector,
PED_UNIT_COMPACT);
percent = ped_unit_format_custom (dev, sector, PED_UNIT_PERCENT);
printf ("%s (%s)\n", compact, percent);
ped_free (compact);
ped_free (percent);
}
static int
partition_print (PedPartition* part)
{
PedFileSystem* fs;
PedConstraint* resize_constraint;
char* flags;
fs = ped_file_system_open (&part->geom);
if (!fs)
return 1;
putchar ('\n');
flags = partition_print_flags (part);
printf (_("Minor: %d\n"), part->num);
printf (_("Flags: %s\n"), flags);
printf (_("File System: %s\n"), fs->type->name);
fputs (_("Size: "), stdout);
print_sector_compact_and_percent (part->geom.length, part->geom.dev);
resize_constraint = ped_file_system_get_resize_constraint (fs);
if (resize_constraint) {
fputs (_("Minimum size: "), stdout);
print_sector_compact_and_percent (resize_constraint->min_size,
part->geom.dev);
fputs (_("Maximum size: "), stdout);
print_sector_compact_and_percent (resize_constraint->max_size,
part->geom.dev);
ped_constraint_destroy (resize_constraint);
}
putchar ('\n');
ped_free (flags);
ped_file_system_close (fs);
return 1;
}
static int
do_print (PedDevice** dev)
{
PedUnit default_unit;
PedDisk* disk;
Table* table;
StrList* row;
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;
char* transport[13] = {"unknown", "scsi", "ide", "dac960",
"cpqarray", "file", "ataraid", "i2o",
"ubd", "dasd", "viodasd", "sx8", "dm"};
char* peek_word;
char* start;
char* end;
char* size;
const char* name;
char* tmp;
wchar_t* table_rendered;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
peek_word = command_line_peek_word ();
if (peek_word) {
if (strncmp (peek_word, "devices", 7) == 0) {
command_line_pop_word();
has_devices_arg = 1;
}
else if (strncmp (peek_word, "free", 4) == 0) {
command_line_pop_word ();
has_free_arg = 1;
}
else if (strncmp (peek_word, "list", 4) == 0 ||
strncmp (peek_word, "all", 3) == 0) {
command_line_pop_word();
has_list_arg = 1;
}
else
has_num_arg = isdigit(peek_word[0]);
ped_free (peek_word);
}
if (has_devices_arg) {
char* dev_name;
PedDevice* current_dev = NULL;
ped_device_probe_all();
while ((current_dev = ped_device_get_next(current_dev))) {
end = ped_unit_format_byte (current_dev,
current_dev->length
* current_dev->sector_size);
printf ("%s (%s)\n", current_dev->path, end);
ped_free (end);
}
dev_name = strdup ((*dev)->path);
ped_device_free_all ();
*dev = ped_device_get (dev_name);
if (!*dev)
return 0;
if (!ped_device_open (*dev))
return 0;
ped_free (dev_name);
return 1;
}
else if (has_list_arg)
return _print_list ();
else if (has_num_arg) {
PedPartition* part = NULL;
int status = 0;
if (command_line_get_partition ("", disk, &part))
status = partition_print (part);
ped_disk_destroy (disk);
return status;
}
start = ped_unit_format (*dev, 0);
default_unit = ped_unit_get_default ();
end = ped_unit_format_byte (*dev, (*dev)->length * (*dev)->sector_size
- (default_unit == PED_UNIT_CHS ||
default_unit == PED_UNIT_CYLINDER));
if (opt_machine_mode) {
switch (default_unit) {
case PED_UNIT_CHS: puts ("CHS;");
break;
case PED_UNIT_CYLINDER: puts ("CYL;");
break;
default: puts ("BYT;");
break;
}
printf ("%s:%s:%s:%lld:%lld:%s:%s;\n",
(*dev)->path, end, transport[(*dev)->type],
(*dev)->sector_size, (*dev)->phys_sector_size,
disk->type->name, (*dev)->model);
} else {
printf (_("Model: %s (%s)\n"),
(*dev)->model, transport[(*dev)->type]);
printf (_("Disk %s: %s\n"), (*dev)->path, end);
printf (_("Sector size (logical/physical): %lldB/%lldB\n"),
(*dev)->sector_size, (*dev)->phys_sector_size);
}
ped_free (start);
ped_free (end);
if (ped_unit_get_default () == PED_UNIT_CHS
|| ped_unit_get_default () == PED_UNIT_CYLINDER) {
PedCHSGeometry* chs = &(*dev)->bios_geom;
char* cyl_size = ped_unit_format_custom (*dev,
chs->heads * chs->sectors,
PED_UNIT_KILOBYTE);
if (opt_machine_mode) {
printf ("%d:%d:%d:%s;\n",
chs->cylinders, chs->heads, chs->sectors, cyl_size);
} else {
printf (_("BIOS cylinder,head,sector geometry: %d,%d,%d. "
"Each cylinder is %s.\n"),
chs->cylinders, chs->heads, chs->sectors, cyl_size);
}
ped_free (cyl_size);
}
if (!opt_machine_mode) {
printf (_("Partition Table: %s\n"), disk->type->name);
putchar ('\n');
}
has_extended = ped_disk_type_check_feature (disk->type,
PED_DISK_TYPE_EXTENDED);
has_name = ped_disk_type_check_feature (disk->type,
PED_DISK_TYPE_PARTITION_NAME);
PedPartition* part;
if (!opt_machine_mode) {
if (ped_unit_get_default() == PED_UNIT_CHS) {
row = str_list_create (_("Number"), _("Start"),
_("End"), NULL);
} else {
row = str_list_create (_("Number"), _("Start"),
_("End"), _("Size"), NULL);
}
if (has_extended)
str_list_append (row, _("Type"));
str_list_append (row, _("File system"));
if (has_name)
str_list_append (row, _("Name"));
str_list_append (row, _("Flags"));
table = table_new (str_list_length(row));
table_add_row_from_strlist (table, row);
for (part = ped_disk_next_partition (disk, NULL); part;
part = ped_disk_next_partition (disk, part)) {
if ((!has_free_arg && !ped_partition_is_active(part)) ||
part->type & PED_PARTITION_METADATA)
continue;
tmp = ped_malloc (4);
if (part->num >= 0)
sprintf (tmp, "%2d ", part->num);
else
sprintf (tmp, "%2s ", "");
row = str_list_create (tmp, NULL);
start = ped_unit_format (*dev, part->geom.start);
end = ped_unit_format_byte (
*dev,
(part->geom.end + 1) * (*dev)->sector_size - 1);
size = ped_unit_format (*dev, part->geom.length);
if (ped_unit_get_default() == PED_UNIT_CHS) {
str_list_append (row, start);
str_list_append (row, end);
} else {
str_list_append (row, start);
str_list_append (row, end);
str_list_append (row, size);
}
if (!(part->type & PED_PARTITION_FREESPACE)) {
if (has_extended) {
name = ped_partition_type_get_name (part->type);
str_list_append (row, name);
}
str_list_append (row, part->fs_type ?
part->fs_type->name : "");
if (has_name) {
name = ped_partition_get_name (part);
str_list_append (row, name);
}
str_list_append (row, partition_print_flags (part));
} else {
if (has_extended)
str_list_append (row, "");
str_list_append (row, _("Free Space"));
if (has_name)
str_list_append (row, "");
str_list_append (row, "");
}
//PED_ASSERT (row.cols == caption.cols)
table_add_row_from_strlist (table, row);
}
table_rendered = table_render (table);
#ifdef ENABLE_NLS
printf("%ls\n", table_rendered);
#else
printf("%s\n", table_rendered);
#endif
ped_free (table_rendered);
table_destroy (table);
} else {
for (part = ped_disk_next_partition (disk, NULL); part;
part = ped_disk_next_partition (disk, part)) {
if ((!has_free_arg && !ped_partition_is_active(part)) ||
part->type & PED_PARTITION_METADATA)
continue;
if (part->num >= 0)
printf ("%d:", part->num);
else
fputs ("1:", stdout);
printf ("%s:", ped_unit_format (*dev, part->geom.start));
printf ("%s:", ped_unit_format_byte (
*dev,
(part->geom.end + 1) *
(*dev)->sector_size - 1));
if (ped_unit_get_default() != PED_UNIT_CHS)
printf ("%s:", ped_unit_format (*dev,
part->geom.length));
if (!(part->type & PED_PARTITION_FREESPACE)) {
if (part->fs_type)
printf ("%s:", part->fs_type->name);
else
putchar (':');
if (has_name)
printf ("%s:", _(ped_partition_get_name (part)));
else
putchar (':');
printf ("%s;\n", partition_print_flags (part));
} else {
puts ("free;");
}
}
}
ped_disk_destroy (disk);
return 1;
ped_disk_destroy (disk);
error:
return 0;
}
static int
_print_list ()
{
PedDevice *current_dev = NULL;
ped_device_probe_all();
while ((current_dev = ped_device_get_next(current_dev))) {
do_print (&current_dev);
putchar ('\n');
}
return 1;
}
static int
do_quit (PedDevice** dev)
{
_done (*dev);
exit (0);
}
static PedPartitionType
_disk_get_part_type_for_sector (PedDisk* disk, PedSector sector)
{
PedPartition* extended;
extended = ped_disk_extended_partition (disk);
if (!extended
|| !ped_geometry_test_sector_inside (&extended->geom, sector))
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
_rescue_add_partition (PedPartition* part)
{
const PedFileSystemType* fs_type;
PedGeometry* probed;
PedExceptionOption ex_opt;
PedConstraint* constraint;
char* found_start;
char* found_end;
fs_type = ped_file_system_probe (&part->geom);
if (!fs_type)
return 0;
probed = ped_file_system_probe_specific (fs_type, &part->geom);
if (!probed)
return 0;
if (!ped_geometry_test_inside (&part->geom, probed)) {
ped_geometry_destroy (probed);
return 0;
}
constraint = ped_constraint_exact (probed);
if (!ped_disk_set_partition_geom (part->disk, part, constraint,
probed->start, probed->end)) {
ped_constraint_destroy (constraint);
return 0;
}
ped_constraint_destroy (constraint);
found_start = ped_unit_format (probed->dev, probed->start);
found_end = ped_unit_format (probed->dev, probed->end);
ex_opt = ped_exception_throw (
PED_EXCEPTION_INFORMATION,
PED_EXCEPTION_YES_NO_CANCEL,
_("A %s %s partition was found at %s -> %s. "
"Do you want to add it to the partition table?"),
fs_type->name, ped_partition_type_get_name (part->type),
found_start, found_end);
ped_geometry_destroy (probed);
ped_free (found_start);
ped_free (found_end);
switch (ex_opt) {
case PED_EXCEPTION_CANCEL: return -1;
case PED_EXCEPTION_NO: return 0;
default: break;
}
ped_partition_set_system (part, fs_type);
ped_disk_commit (part->disk);
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
_rescue_pass (PedDisk* disk, PedGeometry* start_range, PedGeometry* end_range)
{
PedSector start;
PedGeometry start_geom_exact;
PedGeometry entire_dev;
PedConstraint constraint;
PedPartition* part;
PedPartitionType part_type;
part_type = _disk_get_part_type_for_sector (
disk, (start_range->start + end_range->end) / 2);
ped_geometry_init (&entire_dev, disk->dev, 0, disk->dev->length);
ped_timer_reset (g_timer);
ped_timer_set_state_name (g_timer, _("searching for file systems"));
for (start = start_range->start; start <= start_range->end; start++) {
ped_timer_update (g_timer, 1.0 * (start - start_range->start)
/ start_range->length);
ped_geometry_init (&start_geom_exact, disk->dev, start, 1);
ped_constraint_init (
&constraint, ped_alignment_any, ped_alignment_any,
&start_geom_exact, &entire_dev,
1, disk->dev->length);
part = ped_partition_new (disk, part_type, NULL, start,
end_range->end);
if (!part) {
ped_constraint_done (&constraint);
continue;
}
ped_exception_fetch_all ();
if (ped_disk_add_partition (disk, part, &constraint)) {
ped_exception_leave_all ();
switch (_rescue_add_partition (part)) {
case 1:
ped_constraint_done (&constraint);
return 1;
case 0:
ped_disk_remove_partition (disk, part);
break;
case -1:
goto error_remove_partition;
}
} else {
ped_exception_leave_all ();
}
ped_partition_destroy (part);
ped_constraint_done (&constraint);
}
ped_timer_update (g_timer, 1.0);
return 1;
error_remove_partition:
ped_disk_remove_partition (disk, part);
ped_partition_destroy (part);
ped_constraint_done (&constraint);
return 0;
}
static int
do_rescue (PedDevice** dev)
{
PedDisk* disk;
PedSector start = 0, end = 0;
PedSector fuzz;
PedGeometry probe_start_region;
PedGeometry probe_end_region;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!command_line_get_sector (_("Start?"), *dev, &start, NULL))
goto error_destroy_disk;
if (!command_line_get_sector (_("End?"), *dev, &end, NULL))
goto error_destroy_disk;
fuzz = PED_MAX (PED_MIN ((end - start) / 10, MEGABYTE_SECTORS(*dev)),
MEGABYTE_SECTORS(*dev) * 16);
ped_geometry_init (&probe_start_region, *dev,
PED_MAX(start - fuzz, 0),
PED_MIN(2 * fuzz, (*dev)->length - (start - fuzz)));
ped_geometry_init (&probe_end_region, *dev,
PED_MAX(end - fuzz, 0),
PED_MIN(2 * fuzz, (*dev)->length - (end - fuzz)));
if (!_rescue_pass (disk, &probe_start_region, &probe_end_region))
goto error_destroy_disk;
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
static int
do_resize (PedDevice** dev)
{
PedDisk *disk;
PedPartition *part = NULL;
PedFileSystem *fs;
PedConstraint *constraint;
PedSector start, end;
PedGeometry *range_start = NULL, *range_end = NULL;
PedGeometry new_geom;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!command_line_get_partition (_("Partition number?"), disk, &part))
goto error_destroy_disk;
if (part->type != PED_PARTITION_EXTENDED) {
if (!_partition_warn_busy (part))
goto error_destroy_disk;
}
start = part->geom.start;
end = part->geom.end;
if (!command_line_get_sector (_("Start?"), *dev, &start, &range_start))
goto error_destroy_disk;
if (!command_line_get_sector (_("End?"), *dev, &end, &range_end))
goto error_destroy_disk;
if (!ped_geometry_init (&new_geom, *dev, start, end - start + 1))
goto error_destroy_disk;
snap_to_boundaries (&new_geom, &part->geom, disk,
range_start, range_end);
if (part->type == PED_PARTITION_EXTENDED) {
constraint = constraint_from_start_end (*dev,
range_start, range_end);
if (!ped_disk_set_partition_geom (disk, part, constraint,
new_geom.start, new_geom.end))
goto error_destroy_constraint;
ped_partition_set_system (part, NULL);
} else {
fs = ped_file_system_open (&part->geom);
if (!fs)
goto error_destroy_disk;
constraint = constraint_intersect_and_destroy (
ped_file_system_get_resize_constraint (fs),
constraint_from_start_end (
*dev, range_start, range_end));
if (!ped_disk_set_partition_geom (disk, part, constraint,
new_geom.start, new_geom.end))
goto error_close_fs;
if (!ped_file_system_resize (fs, &part->geom, g_timer))
goto error_close_fs;
/* may have changed... eg fat16 -> fat32 */
ped_partition_set_system (part, fs->type);
ped_file_system_close (fs);
}
ped_disk_commit (disk);
ped_constraint_destroy (constraint);
ped_disk_destroy (disk);
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_close_fs:
ped_file_system_close (fs);
error_destroy_constraint:
ped_constraint_destroy (constraint);
error_destroy_disk:
ped_disk_destroy (disk);
error:
if (range_start != NULL)
ped_geometry_destroy (range_start);
if (range_end != NULL)
ped_geometry_destroy (range_end);
return 0;
}
static int
do_rm (PedDevice** dev)
{
PedDisk* disk;
PedPartition* part = NULL;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!command_line_get_partition (_("Partition number?"), disk, &part))
goto error_destroy_disk;
if (!_partition_warn_busy (part))
goto error_destroy_disk;
ped_disk_delete_partition (disk, part);
ped_disk_commit (disk);
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
static int
do_select (PedDevice** dev)
{
PedDevice* new_dev = *dev;
if (!command_line_get_device (_("New device?"), &new_dev))
return 0;
if (!ped_device_open (new_dev))
return 0;
ped_device_close (*dev);
*dev = new_dev;
print_using_dev (*dev);
return 1;
}
static int
do_set (PedDevice** dev)
{
PedDisk* disk;
PedPartition* part = NULL;
PedPartitionFlag flag;
int state;
disk = ped_disk_new (*dev);
if (!disk)
goto error;
if (!command_line_get_partition (_("Partition number?"), disk, &part))
goto error_destroy_disk;
if (!command_line_get_part_flag (_("Flag to Invert?"), part, &flag))
goto error_destroy_disk;
state = (ped_partition_get_flag (part, flag) == 0 ? 1 : 0);
if (!is_toggle_mode) {
if (!command_line_get_state (_("New state?"), &state))
goto error_destroy_disk;
}
if (!ped_partition_set_flag (part, flag, state))
goto error_destroy_disk;
if (!ped_disk_commit (disk))
goto error_destroy_disk;
ped_disk_destroy (disk);
if ((*dev)->type != PED_DEVICE_FILE)
disk_is_modified = 1;
return 1;
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
static int
do_toggle (PedDevice **dev)
{
int result;
is_toggle_mode = 1;
result = do_set (dev);
is_toggle_mode = 0;
return result;
}
static int
do_unit (PedDevice** dev)
{
PedUnit unit = ped_unit_get_default ();
if (!command_line_get_unit (_("Unit?"), &unit))
return 0;
ped_unit_set_default (unit);
return 1;
}
static int
do_version ()
{
printf ("\n%s\n%s",
prog_name,
_(copyright_msg));
return 1;
}
static void
_init_messages ()
{
StrList* list;
int first;
PedFileSystemType* fs_type;
PedDiskType* disk_type;
PedPartitionFlag part_flag;
PedUnit unit;
/* flags */
first = 1;
list = str_list_create (_(flag_msg_start), NULL);
for (part_flag = ped_partition_flag_next (0); part_flag;
part_flag = ped_partition_flag_next (part_flag)) {
if (first)
first = 0;
else
str_list_append (list, ", ");
str_list_append (list,
_(ped_partition_flag_get_name (part_flag)));
}
str_list_append (list, "\n");
flag_msg = str_list_convert (list);
str_list_destroy (list);
/* units */
first = 1;
list = str_list_create (_(unit_msg_start), NULL);
for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) {
if (first)
first = 0;
else
str_list_append (list, ", ");
str_list_append (list, ped_unit_get_name (unit));
}
str_list_append (list, "\n");
unit_msg = str_list_convert (list);
str_list_destroy (list);
/* disk type */
list = str_list_create (_(label_type_msg_start), NULL);
first = 1;
for (disk_type = ped_disk_type_get_next (NULL);
disk_type; disk_type = ped_disk_type_get_next (disk_type)) {
if (disk_type->ops->write == NULL)
continue;
if (first)
first = 0;
else
str_list_append (list, ", ");
str_list_append (list, disk_type->name);
}
str_list_append (list, "\n");
label_type_msg = str_list_convert (list);
str_list_destroy (list);
/* mkfs - file system types */
list = str_list_create (_(fs_type_msg_start), NULL);
first = 1;
for (fs_type = ped_file_system_type_get_next (NULL);
fs_type; fs_type = ped_file_system_type_get_next (fs_type)) {
if (fs_type->ops->create == NULL)
continue;
if (first)
first = 0;
else
str_list_append (list, ", ");
str_list_append (list, fs_type->name);
}
str_list_append (list, "\n");
mkfs_fs_type_msg = str_list_convert (list);
str_list_destroy (list);
/* mkpart - file system types */
list = str_list_create (_(fs_type_msg_start), NULL);
first = 1;
for (fs_type = ped_file_system_type_get_next (NULL);
fs_type; fs_type = ped_file_system_type_get_next (fs_type)) {
if (first)
first = 0;
else
str_list_append (list, ", ");
str_list_append (list, fs_type->name);
}
str_list_append (list, "\n");
mkpart_fs_type_msg = str_list_convert (list);
str_list_destroy (list);
/* resize - file system types */
list = str_list_create (_(resize_msg_start), NULL);
first = 1;
for (fs_type = ped_file_system_type_get_next (NULL);
fs_type; fs_type = ped_file_system_type_get_next (fs_type)) {
if (fs_type->ops->resize == NULL)
continue;
if (first)
first = 0;
else
str_list_append (list, ", ");
str_list_append (list, fs_type->name);
}
str_list_append (list, "\n");
resize_fs_type_msg = str_list_convert (list);
str_list_destroy (list);
}
static void
_done_messages ()
{
free (flag_msg);
free (mkfs_fs_type_msg);
free (mkpart_fs_type_msg);
free (resize_fs_type_msg);
free (label_type_msg);
}
static void
_init_commands ()
{
command_register (commands, command_create (
str_list_create_unique ("check", _("check"), NULL),
do_check,
str_list_create (
_("check NUMBER do a simple check on the file "
"system"),
NULL),
str_list_create (_(number_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("cp", _("cp"), NULL),
do_cp,
str_list_create (
_("cp [FROM-DEVICE] FROM-NUMBER TO-NUMBER copy file system to another "
"partition"),
NULL),
str_list_create (_(number_msg), _(device_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("help", _("help"), NULL),
do_help,
str_list_create (
_("help [COMMAND] print general help, or help "
"on COMMAND"),
NULL),
NULL, 1));
command_register (commands, command_create (
str_list_create_unique ("mklabel", _("mklabel"), "mktable", _("mktable"), NULL),
do_mklabel,
str_list_create (
_("mklabel,mktable LABEL-TYPE create a new disklabel "
"(partition table)"),
NULL),
str_list_create (label_type_msg, NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("mkfs", _("mkfs"), NULL),
do_mkfs,
str_list_create (
_("mkfs NUMBER FS-TYPE make a FS-TYPE file "
"system on partititon NUMBER"),
NULL),
str_list_create (_(number_msg), _(mkfs_fs_type_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("mkpart", _("mkpart"), NULL),
do_mkpart,
str_list_create (
_("mkpart PART-TYPE [FS-TYPE] START END make a partition"),
NULL),
str_list_create (_(part_type_msg),
_(mkpart_fs_type_msg),
_(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));
command_register (commands, command_create (
str_list_create_unique ("mkpartfs", _("mkpartfs"), NULL),
do_mkpartfs,
str_list_create (
_("mkpartfs PART-TYPE FS-TYPE START END make a partition with a "
"file system"),
NULL),
str_list_create (_(part_type_msg), _(start_end_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("move", _("move"), NULL),
do_move,
str_list_create (
_("move NUMBER START END move partition NUMBER"),
NULL),
str_list_create (_(number_msg), _(start_end_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("name", _("name"), NULL),
do_name,
str_list_create (
_("name NUMBER NAME name partition NUMBER as NAME"),
NULL),
str_list_create (_(number_msg), _(name_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("print", _("print"), NULL),
do_print,
str_list_create (
_("print [devices|free|list,all|NUMBER] display the partition table, "
"available devices, free space, all found partitions, or a particular "
"partition"),
NULL),
str_list_create (
_("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));
command_register (commands, command_create (
str_list_create_unique ("quit", _("quit"), NULL),
do_quit,
str_list_create (
_("quit exit program"),
NULL),
NULL, 1));
command_register (commands, command_create (
str_list_create_unique ("rescue", _("rescue"), NULL),
do_rescue,
str_list_create (
_("rescue START END rescue a lost partition near "
"START and END"),
NULL),
str_list_create (_(start_end_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("resize", _("resize"), NULL),
do_resize,
str_list_create (
_("resize NUMBER START END resize partition NUMBER and "
"its file system"),
NULL),
str_list_create (_(number_msg),
_(start_end_msg),
_(resize_fs_type_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("rm", _("rm"), NULL),
do_rm,
str_list_create (
_("rm NUMBER delete partition NUMBER"),
NULL),
str_list_create (_(number_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("select", _("select"), NULL),
do_select,
str_list_create (
_("select DEVICE choose the device to edit"),
NULL),
str_list_create (_(device_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("set", _("set"), NULL),
do_set,
str_list_create (
_("set NUMBER FLAG STATE change the FLAG on partition "
"NUMBER"),
NULL),
str_list_create (_(number_msg), flag_msg, _(state_msg), NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("toggle", _("toggle"), NULL),
do_toggle,
str_list_create (
_("toggle [NUMBER [FLAG]] toggle the state of FLAG on "
"partition NUMBER"),
NULL),
str_list_create (_(number_msg), flag_msg, NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("unit", _("unit"), NULL),
do_unit,
str_list_create (
_("unit UNIT set the default unit to UNIT"),
NULL),
str_list_create (unit_msg, NULL), 1));
command_register (commands, command_create (
str_list_create_unique ("version", _("version"), NULL),
do_version,
str_list_create (
_("version display the version number "
"and copyright information of GNU Parted"),
NULL),
str_list_create (
_("'version' displays copyright and version information corresponding to this "
"copy of GNU Parted\n"),
NULL), 1));
}
static void
_done_commands ()
{
Command** walk;
for (walk = commands; *walk; walk++) {
command_destroy (*walk);
*walk = NULL;
}
}
static void
_init_i18n ()
{
/* intialize i18n */
#ifdef ENABLE_NLS
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#endif /* ENABLE_NLS */
}
void
_version ()
{
version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, VERSION, AUTHORS,
(char *) NULL);
}
static int
_parse_options (int* argc_ptr, char*** argv_ptr)
{
int opt, help = 0, list = 0, version = 0, wrong = 0;
while (1)
{
opt = getopt_long (*argc_ptr, *argv_ptr, "hilmsv",
options, NULL);
if (opt == -1)
break;
switch (opt) {
case 'h': help = 1; break;
case 'l': list = 1; break;
case 'm': opt_machine_mode = 1; break;
case 's': opt_script_mode = 1; break;
case 'v': version = 1; break;
case PRETEND_INPUT_TTY:
pretend_input_tty = 1;
break;
default: wrong = 1; break;
}
}
if (wrong == 1) {
fprintf (stderr,
_("Usage: %s [-hlmsv] [DEVICE [COMMAND [PARAMETERS]]...]\n"),
program_name);
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);
}
*argc_ptr -= optind;
*argv_ptr += optind;
return 1;
}
static PedDevice*
_choose_device (int* argc_ptr, char*** argv_ptr)
{
PedDevice* dev;
/* specified on comand line? */
if (*argc_ptr) {
dev = ped_device_get ((*argv_ptr) [0]);
if (!dev)
return NULL;
(*argc_ptr)--;
(*argv_ptr)++;
} else {
retry:
ped_device_probe_all ();
dev = ped_device_get_next (NULL);
if (!dev) {
if (ped_exception_throw (PED_EXCEPTION_ERROR,
PED_EXCEPTION_RETRY_CANCEL,
_("No device found"))
== PED_EXCEPTION_RETRY)
goto retry;
else
return NULL;
}
}
if (!ped_device_open (dev))
return NULL;
return dev;
}
static PedDevice*
_init (int* argc_ptr, char*** argv_ptr)
{
PedDevice* dev;
#ifdef ENABLE_MTRACE
mtrace();
#endif
_init_i18n ();
if (!init_ui ())
goto error;
_init_messages ();
_init_commands ();
if (!_parse_options (argc_ptr, argv_ptr))
goto error_done_commands;
#ifdef HAVE_GETUID
if (getuid() != 0 && !opt_script_mode) {
puts (_("WARNING: You are not superuser. Watch out for "
"permissions."));
}
#endif
dev = _choose_device (argc_ptr, argv_ptr);
if (!dev)
goto error_done_commands;
g_timer = ped_timer_new (_timer_handler, &timer_context);
if (!g_timer)
goto error_done_commands;
timer_context.last_update = 0;
return dev;
error_done_commands:
_done_commands ();
_done_messages ();
done_ui ();
error:
return NULL;
}
static void
_done (PedDevice* dev)
{
if (dev->boot_dirty && dev->type != PED_DEVICE_FILE) {
ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_OK,
_("You should reinstall your boot loader before "
"rebooting. Read section 4 of the Parted User "
"documentation for more information."));
}
if (!opt_script_mode && !opt_machine_mode && disk_is_modified) {
ped_exception_throw (
PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK,
_("You may need to update /etc/fstab.\n"));
}
ped_device_close (dev);
ped_timer_destroy (g_timer);
_done_commands ();
_done_messages ();
done_ui();
}
int
main (int argc, char** argv)
{
PedDevice* dev;
int status;
program_name = argv[0];
atexit (close_stdout);
dev = _init (&argc, &argv);
if (!dev)
return 1;
if (argc || opt_script_mode)
status = non_interactive_mode (&dev, commands, argc, argv);
else
status = interactive_mode (&dev, commands);
_done (dev);
return !status;
}