From 4ddd86d5d96f75b8b5191add9b1eaeebb9a548bd Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 10 Mar 2014 14:15:36 +0100 Subject: build-sys: ove fdisks to disk-utils Signed-off-by: Karel Zak --- Makefile.am | 1 - disk-utils/Makemodule.am | 73 ++ disk-utils/cfdisk.8 | 145 +++ disk-utils/cfdisk.c | 1842 ++++++++++++++++++++++++++ disk-utils/fdisk-menu.c | 880 +++++++++++++ disk-utils/fdisk.8 | 272 ++++ disk-utils/fdisk.c | 943 ++++++++++++++ disk-utils/fdisk.h | 36 + disk-utils/sfdisk.8 | 650 ++++++++++ disk-utils/sfdisk.c | 3230 ++++++++++++++++++++++++++++++++++++++++++++++ fdisks/Makemodule.am | 77 -- fdisks/cfdisk.8 | 145 --- fdisks/cfdisk.c | 1842 -------------------------- fdisks/fdisk-menu.c | 880 ------------- fdisks/fdisk.8 | 272 ---- fdisks/fdisk.c | 943 -------------- fdisks/fdisk.h | 36 - fdisks/sfdisk.8 | 650 ---------- fdisks/sfdisk.c | 3230 ---------------------------------------------- 19 files changed, 8071 insertions(+), 8076 deletions(-) create mode 100644 disk-utils/cfdisk.8 create mode 100644 disk-utils/cfdisk.c create mode 100644 disk-utils/fdisk-menu.c create mode 100644 disk-utils/fdisk.8 create mode 100644 disk-utils/fdisk.c create mode 100644 disk-utils/fdisk.h create mode 100644 disk-utils/sfdisk.8 create mode 100644 disk-utils/sfdisk.c delete mode 100644 fdisks/Makemodule.am delete mode 100644 fdisks/cfdisk.8 delete mode 100644 fdisks/cfdisk.c delete mode 100644 fdisks/fdisk-menu.c delete mode 100644 fdisks/fdisk.8 delete mode 100644 fdisks/fdisk.c delete mode 100644 fdisks/fdisk.h delete mode 100644 fdisks/sfdisk.8 delete mode 100644 fdisks/sfdisk.c diff --git a/Makefile.am b/Makefile.am index 5eb76ac95..151d483bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -86,7 +86,6 @@ include login-utils/Makemodule.am include sys-utils/Makemodule.am include misc-utils/Makemodule.am include disk-utils/Makemodule.am -include fdisks/Makemodule.am include bash-completion/Makemodule.am diff --git a/disk-utils/Makemodule.am b/disk-utils/Makemodule.am index 580139234..dd9143931 100644 --- a/disk-utils/Makemodule.am +++ b/disk-utils/Makemodule.am @@ -112,6 +112,79 @@ blockdev_SOURCES = disk-utils/blockdev.c blockdev_LDADD = $(LDADD) libcommon.la endif + +if BUILD_FDISK +sbin_PROGRAMS += fdisk +dist_man_MANS += disk-utils/fdisk.8 +fdisk_SOURCES = \ + disk-utils/fdisk.c \ + disk-utils/fdisk.h \ + disk-utils/fdisk-menu.c + +fdisk_LDADD = $(LDADD) libcommon.la libfdisk.la +fdisk_CFLAGS = $(AM_CFLAGS) -I$(ul_libfdisk_incdir) + +if BUILD_LIBBLKID +fdisk_CFLAGS += -I$(ul_libblkid_incdir) +fdisk_LDADD += libblkid.la +endif + +if BUILD_LIBUUID +fdisk_CFLAGS += -I$(ul_libuuid_incdir) +fdisk_LDADD += libuuid.la +endif + +if HAVE_STATIC_FDISK +sbin_PROGRAMS += fdisk.static +fdisk_static_SOURCES = $(fdisk_SOURCES) +fdisk_static_LDFLAGS = -all-static +fdisk_static_CFLAGS = $(fdisk_CFLAGS) +fdisk_static_LDADD = $(fdisk_LDADD) +endif +endif # BUILD_FDISK + + +if !ARCH_SPARC +sbin_PROGRAMS += sfdisk +dist_man_MANS += disk-utils/sfdisk.8 +sfdisk_SOURCES = disk-utils/sfdisk.c +sfdisk_LDADD = $(LDADD) libcommon.la + +if HAVE_STATIC_SFDISK +sbin_PROGRAMS += sfdisk.static +sfdisk_static_SOURCES = $(sfdisk_SOURCES) +sfdisk_static_LDFLAGS = -all-static +sfdisk_static_LDADD = $(sfdisk_LDADD) +endif +endif # ARCH_SPARC + + +if BUILD_CFDISK +sbin_PROGRAMS += cfdisk +dist_man_MANS += disk-utils/cfdisk.8 +cfdisk_SOURCES = disk-utils/cfdisk.c +cfdisk_LDADD = $(LDADD) libcommon.la libfdisk.la +cfdisk_CFLAGS = $(AM_CFLAGS) -I$(ul_libfdisk_incdir) + +if BUILD_LIBUUID +cfdisk_CFLAGS += -I$(ul_libuuid_incdir) +cfdisk_LDADD += libuuid.la +endif + +if BUILD_LIBBLKID +cfdisk_CFLAGS += -I$(ul_libblkid_incdir) +cfdisk_LDADD += libblkid.la +endif + +if HAVE_SLANG +cfdisk_LDADD += -lslang +else +cfdisk_CFLAGS += $(NCURSES_CFLAGS) +cfdisk_LDADD += $(NCURSES_LIBS) +endif +endif # BUILD_CFDISK + + if BUILD_PARTX usrsbin_exec_PROGRAMS += partx addpart delpart resizepart dist_man_MANS += \ diff --git a/disk-utils/cfdisk.8 b/disk-utils/cfdisk.8 new file mode 100644 index 000000000..2fae5925f --- /dev/null +++ b/disk-utils/cfdisk.8 @@ -0,0 +1,145 @@ +.\" cfdisk.8 -- man page for cfdisk +.\" Copyright 1994 Kevin E. Martin (martin@cs.unc.edu) +.\" Copyright (C) 2014 Karel Zak +.\" +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of this +.\" manual under the conditions for verbatim copying, provided that the +.\" entire resulting derived work is distributed under the terms of a +.\" permission notice identical to this one. +.\" +.\" " for hilit mode +.TH CFDISK 8 "March 2014" "util-linux" "System Administration" +.SH NAME +cfdisk \- display or manipulate disk partition table +.SH SYNOPSIS +.B cfdisk +.RB [ options ] +.RI [ device ] +.SH DESCRIPTION +.B cfdisk +is a curses-based program for partitioning any block device. +The default device is /dev/sda. + +Note that +.B cfdisk +provides basic partitioning functionality by user friendly interface. If you +need advanced features then use +.BR fdisk (8). + +Since version 2.25 +.BR cfdisk (8) +supports MBR (DOS), GPT, SUN and SGI disk labels, but it does not provides any +functionality in regards to CHS (Cylinder-Head-Sector) addressing. CHS has +never been important for Linux and this addressing concept does not make any +sense for new devices. + +Since version 2.25 +.BR cfdisk (8) +also does not provide a 'print' command. This functionality is provided by +utils +.BR partx (8) +and +.BR lsblk (8) +in very comfortable and rich way. + +If you want to remove an old partition table from device than use +.BR wipefs (8). + +.SH OPTIONS +.IP "\fB\-h, \-\-help\fP" +Display help text and exit. +.IP "\fB\-L\fR, \fB\-\-color\fR[=\fIwhen\fR]" +Colorize output, enabled by default. The optional argument \fIwhen\fP can be +\fBauto\fR, \fBnever\fR or \fBalways\fR. If the \fIwhen\fR argument is omitted, +then it defaults to \fBauto\fR. +.IP "\fB-V, \-\-version" +Display version information and exit. + +.SH COMMANDS +.B cfdisk +commands can be entered by pressing the desired key (pressing +.I Enter +after the command is not necessary). Here is a list of the available +commands: +.TP +.B b +Toggle bootable flag of the current partition. This allows you to +select which primary partition is bootable on the drive. This command does not +have to available for all partition label types. +.TP +.B d +Delete the current partition. This will convert the current partition +into free space and merge it with any free space immediately +surrounding the current partition. A partition already marked as free +space or marked as unusable cannot be deleted. +.TP +.B h +Print the help screen. +.TP +.B n +Create a new partition from free space. +.B cfdisk +next prompts you for the size of the partition you want to create. +The default size is equal to the entire available free space at the current +position. + +The size may be followed by the multiplicative suffixes KiB=1024, +MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" +is optional, e.g. "K" has the same meaning as "KiB") +.TP +.B q +Quit program. This will exit the program without writing any data to +disk. +.TP +.B t +Change the partition type. By default, new partitions are created as +.I Linux +partitions. +.TP +.B W +Write the partition table to disk (you must enter an uppercase W). Since +this might destroy data on the disk, you must either confirm or deny +the write by entering `yes' or `no'. If you enter `yes', +.B cfdisk +will write the partition table to disk and then tell the kernel to re-read the +partition table from the disk. + +The re-reading of the partition table does not work in some cases. In such a +case you need to inform the kernel about new partitions by +.BR partprobe (8), +.BR kpartx (8) +or reboot the system. +.TP +.IR "Up Arrow" , " Down Arrow" +Move the cursor to the previous or next partition. If there are more +partitions than can be displayed on a screen, you can display the next +(previous) set of partitions by moving down (up) at the last (first) +partition displayed on the screen. + +.PP +All of the commands can be entered with either upper or lower case +letters (except for +.BR W rite). +When in a sub-menu or at a prompt to enter a filename, you can hit the +.I ESC +key to return to the main command line. + +.SH "SEE ALSO" +.BR fdisk (8), +.BR sfdisk (8), +.BR parted (8), +.BR partprobe (8), +.BR partx(8) +.SH AUTHOR +Karel Zak +.PP +The current cfdisk implemntation is based on the original cfdisk +from Kevin E. Martin (martin@cs.unc.edu). + +.SH AVAILABILITY +The cfdisk command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/disk-utils/cfdisk.c b/disk-utils/cfdisk.c new file mode 100644 index 000000000..0fd64bf61 --- /dev/null +++ b/disk-utils/cfdisk.c @@ -0,0 +1,1842 @@ +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SLANG_H +# include +#elif defined(HAVE_SLANG_SLANG_H) +# include +#endif + +#ifdef HAVE_SLCURSES_H +# include +#elif defined(HAVE_SLANG_SLCURSES_H) +# include +#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR) +# include +#elif defined(HAVE_NCURSES_H) +# include +#elif defined(HAVE_NCURSES_NCURSES_H) +# include +#endif + +#ifdef HAVE_WIDECHAR +# include +#endif + +#include "c.h" +#include "closestream.h" +#include "nls.h" +#include "strutils.h" +#include "xalloc.h" +#include "mbsalign.h" +#include "colors.h" + +#include "fdiskP.h" + +#define ARROW_CURSOR_STRING " >> " +#define ARROW_CURSOR_DUMMY " " +#define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1) + +#define MENU_PADDING 2 +#define TABLE_START_LINE 4 +#define MENU_START_LINE ((size_t) LINES - 5) +#define INFO_LINE ((size_t) LINES - 2) +#define HINT_LINE ((size_t) LINES - 1) + +#define CFDISK_ERR_ESC 5000 + +#ifndef KEY_ESC +# define KEY_ESC '\033' +#endif +#ifndef KEY_DELETE +# define KEY_DELETE '\177' +#endif + +/* colors */ +enum { + CFDISK_CL_NONE = 0, + CFDISK_CL_WARNING, + CFDISK_CL_FREESPACE, +}; +static const int color_pairs[][2] = { + /* color foreground, background */ + [CFDISK_CL_WARNING] = { COLOR_RED, -1 }, + [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 } +}; + +struct cfdisk; + +static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx); +static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx); +static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item); +static struct cfdisk_menu *menu_pop(struct cfdisk *cf); + +static int ui_refresh(struct cfdisk *cf); +static void ui_warnx(const char *fmt, ...); +static void ui_warn(const char *fmt, ...); +static void ui_info(const char *fmt, ...); +static void ui_draw_menu(struct cfdisk *cf); +static int ui_menu_move(struct cfdisk *cf, int key); + +static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res, + uintmax_t low, uintmax_t up); + +static int ui_enabled; + +/* menu item */ +struct cfdisk_menuitem { + int key; /* keyboard shortcut */ + const char *name; /* item name */ + const char *desc; /* item description (hint) */ + void *userdata; +}; + +/* menu */ +struct cfdisk_menu { + char *title; /* optional menu title */ + struct cfdisk_menuitem *items; /* array with menu items */ + char *ignore;/* string with keys to ignore */ + size_t width; /* maximal width of the menu item */ + size_t nitems; /* number of the active menu items */ + size_t page_sz;/* when menu longer than screen */ + size_t idx; /* the current menu item */ + struct cfdisk_menu *prev; + + /* @ignore keys generator */ + int (*ignore_cb) (struct cfdisk *, char *, size_t); + + unsigned int vertical : 1; /* enable vertical mode */ +}; + +/* main menu */ +static struct cfdisk_menuitem main_menuitems[] = { + { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") }, + { 'd', N_("Delete"), N_("Delete the current partition") }, + { 'n', N_("New"), N_("Create new partition from free space") }, + { 'q', N_("Quit"), N_("Quit program without writing partition table") }, + { 't', N_("Type"), N_("Change the partition type") }, + { 'h', N_("Help"), N_("Print help screen") }, + { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") }, + { 0, NULL, NULL } +}; + +/* top level control struct */ +struct cfdisk { + struct fdisk_context *cxt; /* libfdisk context */ + struct fdisk_table *table; /* partition table */ + struct cfdisk_menu *menu; /* the current menu */ + + int *cols; /* output columns */ + size_t ncols; /* number of columns */ + + char *linesbuf; /* table as string */ + size_t linesbufsz; /* size of the tb_buf */ + + char **lines; /* array with lines */ + size_t nlines; /* number of lines */ + size_t lines_idx; /* current line <0..N>, exclude header */ + size_t page_sz; +}; + +/* Initialize output columns -- we follow libcfdisk columns (usually specific + * to the label type. + */ +static int cols_init(struct cfdisk *cf) +{ + assert(cf); + + free(cf->cols); + cf->cols = NULL; + cf->ncols = 0; + + return fdisk_get_columns(cf->cxt, 0, &cf->cols, &cf->ncols); +} + +/* It would be possible to use fdisk_table_to_string(), but we want some + * extension to the output format, so let's do it without libfdisk + */ +static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb) +{ + const struct fdisk_column *col; + struct fdisk_partition *pa; + struct fdisk_label *lb; + struct fdisk_iter *itr = NULL; + struct tt *tt = NULL; + char *res = NULL; + size_t i; + int tree = 0; + struct tt_line *ln, *ln_cont = NULL; + + DBG(FRONTEND, dbgprint("table: convert to string")); + + assert(cf); + assert(cf->cxt); + assert(cf->cols); + assert(tb); + + lb = fdisk_context_get_label(cf->cxt, NULL); + assert(lb); + + itr = fdisk_new_iter(FDISK_ITER_FORWARD); + if (!itr) + goto done; + + /* get container (e.g. extended partition) */ + while (fdisk_table_next_partition(tb, itr, &pa) == 0) { + if (fdisk_partition_is_nested(pa)) { + DBG(FRONTEND, dbgprint("table: nested detected, using tree")); + tree = TT_FL_TREE; + break; + } + } + + tt = tt_new_table(TT_FL_FREEDATA | TT_FL_MAX | tree); + if (!tt) + goto done; + + /* headers */ + for (i = 0; i < cf->ncols; i++) { + col = fdisk_label_get_column(lb, cf->cols[i]); + if (col) { + int fl = col->tt_flags; + if (tree && col->id == FDISK_COL_DEVICE) + fl |= TT_FL_TREE; + tt_define_column(tt, col->name, col->width, fl); + } + } + + /* data */ + fdisk_reset_iter(itr, FDISK_ITER_FORWARD); + + while (fdisk_table_next_partition(tb, itr, &pa) == 0) { + struct tt_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL; + + ln = tt_add_line(tt, parent); + if (!ln) + goto done; + for (i = 0; i < cf->ncols; i++) { + char *cdata = NULL; + col = fdisk_label_get_column(lb, cf->cols[i]); + if (!col) + continue; + if (fdisk_partition_to_string(pa, cf->cxt, col->id, &cdata)) + continue; + tt_line_set_data(ln, i, cdata); + } + if (tree && fdisk_partition_is_container(pa)) + ln_cont = ln; + + tt_line_set_userdata(ln, (void *) pa); + fdisk_ref_partition(pa); + } + + if (tt_is_empty(tt)) + goto done; + + tt_set_termreduce(tt, ARROW_CURSOR_WIDTH); + tt_print_table_to_string(tt, &res); + + /* tt_* code might to reorder lines, let's reorder @tb according to the + * final output (it's no problem because partitions are addressed by + * parno stored within struct fdisk_partition) */ + + /* remove all */ + fdisk_reset_iter(itr, FDISK_ITER_FORWARD); + while (fdisk_table_next_partition(tb, itr, &pa) == 0) + fdisk_table_remove_partition(tb, pa); + + /* add all in the right order */ + i = 0; + while (tt_get_output_line(tt, i++, &ln) == 0) { + struct fdisk_partition *pa = tt_line_get_userdata(ln); + + fdisk_table_add_partition(tb, pa); + fdisk_unref_partition(pa); + } +done: + tt_free_table(tt); + fdisk_free_iter(itr); + + return res; +} + +/* + * Read data about partitions from libfdisk and prepare output lines. + */ +static int lines_refresh(struct cfdisk *cf) +{ + int rc; + char *p; + size_t i; + + assert(cf); + + DBG(FRONTEND, dbgprint("refreshing buffer")); + + free(cf->linesbuf); + free(cf->lines); + cf->linesbuf = NULL; + cf->linesbufsz = 0; + cf->lines = NULL; + cf->nlines = 0; + + fdisk_unref_table(cf->table); + cf->table = NULL; + + /* read partitions and free spaces into cf->table */ + rc = fdisk_get_partitions(cf->cxt, &cf->table); + if (!rc) + rc = fdisk_get_freespaces(cf->cxt, &cf->table); + if (rc) + return rc; + + cf->linesbuf = table_to_string(cf, cf->table); + if (!cf->linesbuf) + return -ENOMEM; + + cf->linesbufsz = strlen(cf->linesbuf); + cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */ + cf->page_sz = 0; + + if (MENU_START_LINE - TABLE_START_LINE < cf->nlines) + cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1; + + cf->lines = xcalloc(cf->nlines, sizeof(char *)); + + for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) { + cf->lines[i] = p; + p = strchr(p, '\n'); + if (p) { + *p = '\0'; + p++; + } + } + + return 0; +} + +static struct fdisk_partition *get_current_partition(struct cfdisk *cf) +{ + assert(cf); + assert(cf->table); + + return fdisk_table_get_partition(cf->table, cf->lines_idx); +} + +static int is_freespace(struct cfdisk *cf, size_t i) +{ + struct fdisk_partition *pa; + + assert(cf); + assert(cf->table); + + pa = fdisk_table_get_partition(cf->table, i); + return fdisk_partition_is_freespace(pa); +} + +/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's + * responseback to libfdisk + */ +static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf) +{ + struct cfdisk_menuitem *d, *cm; + int key; + size_t i = 0, nitems; + const char *name, *desc; + + assert(ask); + assert(cf); + + /* create cfdisk menu according to libfdisk ask-menu, note that the + * last cm[] item has to be empty -- so nitems + 1 */ + nitems = fdisk_ask_menu_get_nitems(ask); + cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem)); + + for (i = 0; i < nitems; i++) { + if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc)) + break; + cm[i].key = key; + cm[i].desc = desc; + cm[i].name = name; + } + + /* make the new menu active */ + menu_push(cf, cm); + ui_draw_menu(cf); + refresh(); + + /* wait for keys */ + do { + int key = getch(); + + if (ui_menu_move(cf, key) == 0) + continue; + + switch (key) { + case KEY_ENTER: + case '\n': + case '\r': + d = menu_get_menuitem(cf, cf->menu->idx); + if (d) + fdisk_ask_menu_set_result(ask, d->key); + menu_pop(cf); + free(cm); + return 0; + } + } while (1); + + menu_pop(cf); + free(cm); + return -1; +} + +/* libfdisk callback + */ +static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, + void *data __attribute__((__unused__))) +{ + int rc = 0; + + assert(cxt); + assert(ask); + + switch(fdisk_ask_get_type(ask)) { + case FDISK_ASKTYPE_INFO: + ui_info(fdisk_ask_print_get_mesg(ask)); + break; + case FDISK_ASKTYPE_WARNX: + ui_warnx(fdisk_ask_print_get_mesg(ask)); + break; + case FDISK_ASKTYPE_WARN: + ui_warn(fdisk_ask_print_get_mesg(ask)); + break; + case FDISK_ASKTYPE_MENU: + ask_menu(ask, (struct cfdisk *) data); + break; + default: + ui_warnx(_("internal error: unsupported dialog type %d"), + fdisk_ask_get_type(ask)); + return -EINVAL; + } + return rc; +} + +static int ui_end(void) +{ + if (!ui_enabled) + return -EINVAL; + +#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H) + SLsmg_gotorc(LINES - 1, 0); + SLsmg_refresh(); +#else + mvcur(0, COLS - 1, LINES-1, 0); +#endif + nl(); + endwin(); + printf("\n"); + ui_enabled = 0; + return 0; +} + +static void ui_vprint_center(int line, int attrs, const char *fmt, va_list ap) +{ + size_t width; + char *buf = NULL; + + move(line, 0); + clrtoeol(); + + xvasprintf(&buf, fmt, ap); + + width = mbs_safe_width(buf); + if (width > (size_t) COLS) { + char *p = strrchr(buf + COLS, ' '); + if (!p) + p = buf + COLS; + *p = '\0'; + if (line + 1 >= LINES) + line--; + attron(attrs); + mvaddstr(line, 0, buf); + mvaddstr(line + 1, 0, p+1); + attroff(attrs); + } else { + attron(attrs); + mvaddstr(line, (COLS - width) / 2, buf); + attroff(attrs); + } + free(buf); +} + +static void ui_center(int line, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + ui_vprint_center(line, 0, fmt, ap); + va_end(ap); +} + +static void ui_warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (ui_enabled) + ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt, ap); + else + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void ui_warn(const char *fmt, ...) +{ + char *fmt_m; + va_list ap; + + xasprintf(&fmt_m, "%s: %m", fmt); + + va_start(ap, fmt); + if (ui_enabled) + ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt_m, ap); + else + vfprintf(stderr, fmt_m, ap); + va_end(ap); + free(fmt_m); +} + +static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...) + { + va_list ap; + ui_end(); + + va_start(ap, fmt); + fprintf(stderr, "%s: ", program_invocation_short_name); + vfprintf(stderr, fmt, ap); + va_end(ap); + + exit(rc); +} + +static void ui_info(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (ui_enabled) + ui_vprint_center(INFO_LINE, A_BOLD, fmt, ap); + else + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +static void ui_clean_info(void) +{ + move(INFO_LINE, 0); + clrtoeol(); +} + +static void ui_hint(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (ui_enabled) + ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap); + else + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +static void ui_clean_hint(void) +{ + move(HINT_LINE, 0); + clrtoeol(); +} + +static void die_on_signal(int dummy __attribute__((__unused__))) +{ + DBG(FRONTEND, dbgprint("die on signal.")); + ui_end(); + exit(EXIT_FAILURE); +} + +static void menu_update_ignore(struct cfdisk *cf) +{ + char ignore[128] = { 0 }; + int i = 0; + struct cfdisk_menu *m; + struct cfdisk_menuitem *d, *org; + size_t idx; + + assert(cf); + assert(cf->menu); + assert(cf->menu->ignore_cb); + + m = cf->menu; + org = menu_get_menuitem(cf, m->idx); + + DBG(FRONTEND, dbgprint("menu: update menu ignored keys")); + + i = m->ignore_cb(cf, ignore, sizeof(ignore)); + ignore[i] = '\0'; + + /* return if no change */ + if ( (!m->ignore && !*ignore) + || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) { + return; + } + + free(m->ignore); + m->ignore = xstrdup(ignore); + m->nitems = 0; + + for (d = m->items; d->name; d++) { + if (m->ignore && strchr(m->ignore, d->key)) + continue; + m->nitems++; + } + + /* refresh menu index to be at the same menuitem or go to the first */ + if (org && menu_get_menuitem_by_key(cf, org->key, &idx)) + m->idx = idx; + else + m->idx = 0; + + m->page_sz = m->nitems / (LINES - 4) ? LINES - 4 : 0; +} + +static struct cfdisk_menu *menu_push( + struct cfdisk *cf, + struct cfdisk_menuitem *items) +{ + struct cfdisk_menu *m = xcalloc(1, sizeof(*m)); + struct cfdisk_menuitem *d; + + assert(cf); + + DBG(FRONTEND, dbgprint("menu: new menu")); + + m->prev = cf->menu; + m->items = items; + + for (d = m->items; d->name; d++) { + const char *name = _(d->name); + size_t len = mbs_safe_width(name); + if (len > m->width) + m->width = len; + m->nitems++; + } + + cf->menu = m; + m->page_sz = m->nitems / (LINES - 4) ? LINES - 4 : 0; + return m; +} + +static struct cfdisk_menu *menu_pop(struct cfdisk *cf) +{ + struct cfdisk_menu *m = NULL; + + assert(cf); + + DBG(FRONTEND, dbgprint("menu: rem menu")); + + if (cf->menu) { + m = cf->menu->prev; + free(cf->menu->ignore); + free(cf->menu->title); + free(cf->menu); + } + cf->menu = m; + return cf->menu; +} + +static void menu_set_title(struct cfdisk_menu *m, const char *title) +{ + char *str = NULL; + + if (title) { + size_t len = mbs_safe_width(title); + if (len + 3 > m->width) + m->width = len + 3; + str = xstrdup(title); + } + m->title = str; +} + + +static int ui_init(struct cfdisk *cf __attribute__((__unused__))) +{ + struct sigaction sa; + + DBG(FRONTEND, dbgprint("ui: init")); + + /* setup SIGCHLD handler */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = die_on_signal; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + ui_enabled = 1; + initscr(); + + if (colors_wanted() && has_colors()) { + size_t i; + + start_color(); + use_default_colors(); + + for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */ + init_pair(i, color_pairs[i][0], color_pairs[i][1]); + } + + cbreak(); + noecho(); + nonl(); + curs_set(0); + keypad(stdscr, TRUE); + + return 0; +} + +static size_t menuitem_get_line(struct cfdisk *cf, size_t idx) +{ + struct cfdisk_menu *m = cf->menu; + + if (m->vertical) { + if (!m->page_sz) /* small menu */ + return (LINES - (cf->menu->nitems + 1)) / 2 + idx; + return (idx % m->page_sz) + 1; + } else { + size_t len = m->width + 4 + MENU_PADDING; /* item width */ + size_t items = COLS / len; /* items per line */ + + return MENU_START_LINE + ((idx / items)); + } +} + +static int menuitem_get_column(struct cfdisk *cf, size_t idx) +{ + if (cf->menu->vertical) { + size_t nc = cf->menu->width + MENU_PADDING; + if ((size_t) COLS <= nc) + return 0; + return (COLS - nc) / 2; + } else { + size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */ + size_t items = COLS / len; /* items per line */ + size_t extra = items < cf->menu->nitems ? /* extra space on line */ + COLS % len : /* - multi-line menu */ + COLS - (cf->menu->nitems * len); /* - one line menu */ + + extra += MENU_PADDING; /* add padding after last item to extra */ + + if (idx < items) + return (idx * len) + (extra / 2); + return ((idx % items) * len) + (extra / 2); + } +} + +static int menuitem_on_page(struct cfdisk *cf, size_t idx) +{ + struct cfdisk_menu *m = cf->menu; + + if (m->page_sz == 0 || + m->idx / m->page_sz == idx / m->page_sz) + return 1; + return 0; +} + +static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx) +{ + struct cfdisk_menuitem *d; + size_t i; + + for (i = 0, d = cf->menu->items; d->name; d++) { + if (cf->menu->ignore && strchr(cf->menu->ignore, d->key)) + continue; + if (i++ == idx) + return d; + } + + return NULL; +} + +static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, + int key, size_t *idx) +{ + struct cfdisk_menuitem *d; + + for (*idx = 0, d = cf->menu->items; d->name; d++) { + if (cf->menu->ignore && strchr(cf->menu->ignore, d->key)) + continue; + if (key == d->key) + return d; + (*idx)++; + } + + return NULL; +} + +static void ui_draw_menuitem(struct cfdisk *cf, + struct cfdisk_menuitem *d, + size_t idx) +{ + char buf[80 * MB_CUR_MAX]; + const char *name; + size_t width = cf->menu->width + 2; /* 2 = blank around string */ + int ln, cl, vert = cf->menu->vertical; + + if (!menuitem_on_page(cf, idx)) + return; /* no visible item */ + ln = menuitem_get_line(cf, idx); + cl = menuitem_get_column(cf, idx); + + name = _(d->name); + mbsalign(name, buf, sizeof(buf), &width, + vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER, + 0); + + DBG(FRONTEND, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'", + cl, ln, buf)); + + if (vert) { + mvaddch(ln, cl - 1, ACS_VLINE); + mvaddch(ln, cl + cf->menu->width + 4, ACS_VLINE); + } + + if (cf->menu->idx == idx) { + standout(); + mvprintw(ln, cl, vert ? " %s " : "[%s]", buf); + standend(); + if (d->desc) + ui_hint(d->desc); + } else + mvprintw(ln, cl, vert ? " %s " : "[%s]", buf); +} + +static void ui_draw_menu(struct cfdisk *cf) +{ + struct cfdisk_menuitem *d; + struct cfdisk_menu *m; + size_t i = 0; + size_t ln = menuitem_get_line(cf, 0); + size_t nlines; + + assert(cf); + assert(cf->menu); + + DBG(FRONTEND, dbgprint("ui: menu: draw start")); + + m = cf->menu; + + if (m->vertical) + nlines = m->page_sz ? m->page_sz : m->nitems; + else + nlines = menuitem_get_line(cf, m->nitems); + + for (i = ln; i <= ln + nlines; i++) { + move(i, 0); + clrtoeol(); + } + + if (m->ignore_cb) + menu_update_ignore(cf); + i = 0; + while ((d = menu_get_menuitem(cf, i))) + ui_draw_menuitem(cf, d, i++); + + if (m->vertical) { + size_t cl = menuitem_get_column(cf, 0); + size_t curpg = m->page_sz ? m->idx / m->page_sz : 0; + + /* corners and horizontal lines */ + mvaddch(ln - 1, cl - 1, ACS_ULCORNER); + mvaddch(ln + nlines, cl - 1, ACS_LLCORNER); + + for (i = 0; i < m->width + 4; i++) { + mvaddch(ln - 1, cl + i, ACS_HLINE); + mvaddch(ln + nlines, cl + i, ACS_HLINE); + } + + mvaddch(ln - 1, cl + i, ACS_URCORNER); + mvaddch(ln + nlines, cl + i, ACS_LRCORNER); + + /* draw also lines around empty lines on last page */ + if (m->page_sz && + m->nitems / m->page_sz == m->idx / m->page_sz) { + for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) { + mvaddch(i, cl - 1, ACS_VLINE); + mvaddch(i, cl + cf->menu->width + 4, ACS_VLINE); + } + } + if (m->title) { + attron(A_BOLD); + mvprintw(ln - 1, cl, " %s ", m->title); + attroff(A_BOLD); + } + if (curpg != 0) + mvaddch(ln - 1, cl + m->width + 3, ACS_UARROW); + if (m->page_sz && curpg < m->nitems / m->page_sz) + mvaddch(ln + nlines, cl + m->width + 3, ACS_DARROW); + } + + DBG(FRONTEND, dbgprint("ui: menu: draw end.")); +} + +static void ui_menu_goto(struct cfdisk *cf, int where) +{ + struct cfdisk_menuitem *d; + size_t old; + + /* stop and begin/end for vertical menus */ + if (cf->menu->vertical) { + if (where < 0) + where = 0; + else if (where > (int) cf->menu->nitems - 1) + where = cf->menu->nitems - 1; + } else { + /* continue from begin/end */ + if (where < 0) + where = cf->menu->nitems - 1; + else if ((size_t) where > cf->menu->nitems - 1) + where = 0; + } + if ((size_t) where == cf->menu->idx) + return; + + ui_clean_info(); + + old = cf->menu->idx; + cf->menu->idx = where; + + if (!menuitem_on_page(cf, old)) { + ui_draw_menu(cf); + return; + } + + d = menu_get_menuitem(cf, old); + ui_draw_menuitem(cf, d, old); + + d = menu_get_menuitem(cf, where); + ui_draw_menuitem(cf, d, where); +} + +static int ui_menu_move(struct cfdisk *cf, int key) +{ + struct cfdisk_menu *m; + + assert(cf); + assert(cf->menu); + + m = cf->menu; + + DBG(FRONTEND, dbgprint("ui: menu move key >%c<.", key)); + + if (m->vertical) + { + switch (key) { + case KEY_DOWN: + case '\016': /* ^N */ + case 'j': /* Vi-like alternative */ + ui_menu_goto(cf, m->idx + 1); + return 0; + case KEY_UP: + case '\020': /* ^P */ + case 'k': /* Vi-like alternative */ + ui_menu_goto(cf, (int) m->idx - 1); + return 0; + case KEY_PPAGE: + if (m->page_sz) { + ui_menu_goto(cf, (int) m->idx - m->page_sz); + return 0; + } + case KEY_HOME: + ui_menu_goto(cf, 0); + return 0; + case KEY_NPAGE: + if (m->page_sz) { + ui_menu_goto(cf, m->idx + m->page_sz); + return 0; + } + case KEY_END: + ui_menu_goto(cf, m->nitems); + return 0; + } + } else { + switch (key) { + case KEY_RIGHT: + case '\t': + ui_menu_goto(cf, m->idx + 1); + return 0; + case KEY_LEFT: +#ifdef KEY_BTAB + case KEY_BTAB: +#endif + ui_menu_goto(cf, (int) m->idx - 1); + return 0; + } + } + + return 1; /* key irrelevant for menu move */ +} + +static int partition_on_page(struct cfdisk *cf, size_t i) +{ + if (cf->page_sz == 0 || + cf->lines_idx / cf->page_sz == i / cf->page_sz) + return 1; + return 0; +} + +static void ui_draw_partition(struct cfdisk *cf, size_t i) +{ + int ln = TABLE_START_LINE + 1 + i; /* skip table header */ + int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */ + int cur = cf->lines_idx == i; + size_t curpg = 0; + + if (cf->page_sz) { + if (!partition_on_page(cf, i)) + return; + ln = TABLE_START_LINE + (i % cf->page_sz) + 1; + curpg = cf->lines_idx / cf->page_sz; + } + + DBG(FRONTEND, dbgprint( + "ui: draw partition %zu [page_sz=%zu, " + "line=%d, idx=%zu]", + i, cf->page_sz, ln, cf->lines_idx)); + + if (cur) { + attron(A_REVERSE); + mvaddstr(ln, 0, ARROW_CURSOR_STRING); + mvaddstr(ln, cl, cf->lines[i + 1]); + attroff(A_REVERSE); + } else { + int at = 0; + + if (is_freespace(cf, i)) { + attron(COLOR_PAIR(CFDISK_CL_FREESPACE)); + at = 1; + } + mvaddstr(ln, 0, ARROW_CURSOR_DUMMY); + mvaddstr(ln, cl, cf->lines[i + 1]); + if (at) + attroff(COLOR_PAIR(CFDISK_CL_FREESPACE)); + } + + if ((size_t) ln == MENU_START_LINE - 1 && + cf->page_sz && curpg < cf->nlines / cf->page_sz) { + if (cur) + attron(A_REVERSE); + mvaddch(ln, COLS - 1, ACS_DARROW); + mvaddch(ln, 0, ACS_DARROW); + if (cur) + attroff(A_REVERSE); + } +} + +static int ui_draw_table(struct cfdisk *cf) +{ + int cl = ARROW_CURSOR_WIDTH; + size_t i, nparts = fdisk_table_get_nents(cf->table); + size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0; + + DBG(FRONTEND, dbgprint("ui: draw table")); + + for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) { + move(i, 0); + clrtoeol(); + } + + if ((size_t) cf->lines_idx > nparts - 1) + cf->lines_idx = nparts ? nparts - 1 : 0; + + /* print header */ + attron(A_BOLD); + mvaddstr(TABLE_START_LINE, cl, cf->lines[0]); + attroff(A_BOLD); + + /* print partitions */ + for (i = 0; i < nparts; i++) + ui_draw_partition(cf, i); + + if (curpg != 0) { + mvaddch(TABLE_START_LINE, COLS - 1, ACS_UARROW); + mvaddch(TABLE_START_LINE, 0, ACS_UARROW); + } + if (cf->page_sz && curpg < cf->nlines / cf->page_sz) { + mvaddch(MENU_START_LINE - 1, COLS - 1, ACS_DARROW); + mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW); + } + return 0; +} + +static int ui_table_goto(struct cfdisk *cf, int where) +{ + size_t old; + size_t nparts = fdisk_table_get_nents(cf->table); + + DBG(FRONTEND, dbgprint("ui: goto table %d", where)); + + if (where < 0) + where = 0; + else if ((size_t) where > nparts - 1) + where = nparts - 1; + + if ((size_t) where == cf->lines_idx) + return 0; + + old = cf->lines_idx; + cf->lines_idx = where; + + if (!partition_on_page(cf, old) ||!partition_on_page(cf, where)) + ui_draw_table(cf); + else { + ui_draw_partition(cf, old); /* cleanup old */ + ui_draw_partition(cf, where); /* draw new */ + } + ui_clean_info(); + ui_draw_menu(cf); + refresh(); + return 0; +} + +static int ui_refresh(struct cfdisk *cf) +{ + char *id = NULL; + uint64_t bytes = cf->cxt->total_sectors * cf->cxt->sector_size; + char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE + | SIZE_SUFFIX_3LETTER, bytes); + erase(); + + if (!ui_enabled) + return -EINVAL; + + /* header */ + attron(A_BOLD); + ui_center(0, _("Disk: %s"), cf->cxt->dev_path); + attroff(A_BOLD); + ui_center(1, _("Size: %s, %ju bytes, %ju sectors"), + strsz, bytes, (uintmax_t) cf->cxt->total_sectors); + if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id) + ui_center(2, _("Label: %s, identifier: %s"), + cf->cxt->label->name, id); + else + ui_center(2, _("Label: %s"), cf->cxt->label->name); + free(strsz); + + ui_draw_table(cf); + ui_draw_menu(cf); + refresh(); + return 0; +} + +static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt, + const char *hint, char *buf, size_t len) +{ + size_t cells = 0; + ssize_t i = 0, rc = -1; + wint_t c; + int ln = MENU_START_LINE, cl = 1; + + assert(cf); + assert(buf); + assert(len); + + move(ln, 0); + clrtoeol(); + + if (prompt) { + mvaddstr(ln, cl, prompt); + cl += mbs_safe_width(prompt); + } + + /* default value */ + if (*buf) { + i = strlen(buf); + cells = mbs_safe_width(buf); + mvaddstr(ln, cl, buf); + } + + if (hint) + ui_hint(hint); + else + ui_clean_hint(); + + move(ln, cl + cells); + curs_set(1); + refresh(); + + while (1) { +#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \ + defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR) + if (get_wch(&c) == ERR) { +#else + if ((c = getch()) == ERR) { +#endif + if (!isatty(STDIN_FILENO)) + exit(2); + else + goto done; + } + if (c == '\r' || c == '\n' || c == KEY_ENTER) + break; + + switch (c) { + case KEY_ESC: + rc = -CFDISK_ERR_ESC; + goto done; + case KEY_DELETE: + case '\b': + case KEY_BACKSPACE: + if (i > 0) { + cells--; + i = mbs_truncate(buf, &cells); + if (i < 0) + goto done; + mvaddch(ln, cl + cells, ' '); + move(ln, cl + cells); + } else + beep(); + break; + default: +#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR) + if (i + 1 < (ssize_t) len && iswprint(c)) { + wchar_t wc = (wchar_t) c; + char s[MB_CUR_MAX + 1]; + int sz = wctomb(s, wc); + + if (sz > 0 && sz + i < (ssize_t) len) { + s[sz] = '\0'; + mvaddnstr(ln, cl + cells, s, sz); + memcpy(buf + i, s, sz); + i += sz; + buf[i] = '\0'; + cells += wcwidth(wc); + } else + beep(); + } +#else + if (i + 1 < (ssize_t) len && isprint(c)) { + mvaddch(ln, cl + cells, c); + str[i++] = c; + str[i] = '\0'; + cells++; + } +#endif + else + beep(); + } + refresh(); + } + + rc = i; /* success */ +done: + move(ln, 0); + clrtoeol(); + curs_set(0); + refresh(); + + return rc; +} + +/* @res is default value as well as result in bytes */ +static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res, + uintmax_t low, uintmax_t up) +{ + char buf[128]; + uintmax_t user = 0; + ssize_t rc; + char *dflt = size_to_human_string(0, *res); + + DBG(FRONTEND, dbgprint("ui: get_size (default=%ju)", *res)); + + ui_clean_info(); + + do { + int pwr = 0, insec = 0; + + snprintf(buf, sizeof(buf), "%s", dflt); + rc = ui_get_string(cf, prompt, + _("May be followed by {M,B,G,T}iB " + "(the \"iB\" is optional) or S for sectors."), + buf, sizeof(buf)); + if (rc == 0) { + ui_warnx(_("Please, specify size.")); + continue; /* nothing specified */ + } else if (rc == -CFDISK_ERR_ESC) + break; /* cancel dialog */ + + if (strcmp(buf, dflt) == 0) + user = *res, rc = 0; /* no change, use default */ + else { + size_t len = strlen(buf); + if (buf[len - 1] == 'S') { + insec = 1; + buf[len - 1] = '\0'; + } + rc = parse_size(buf, &user, &pwr); /* parse */ + } + + if (rc == 0) { + DBG(FRONTEND, dbgprint("ui: get_size user=%ju, power=%d, sectors=%s", + user, pwr, insec ? "yes" : "no")); + if (insec) + user *= cf->cxt->sector_size; + if (user < low) { + ui_warnx(_("Minimal size is %ju"), low); + rc = -ERANGE; + } + if (user > up && pwr && user < up + (1ULL << pwr * 10)) + /* ignore when the user specified size overflow + * with in range specified by suffix (e.g. MiB) */ + user = up; + + if (user > up) { + ui_warnx(_("Maximal size is %ju bytes."), up); + rc = -ERANGE; + } + } else + ui_warnx(_("Failed to parse size.")); + } while (rc != 0); + + if (rc == 0) + *res = user; + free(dflt); + + DBG(FRONTEND, dbgprint("ui: get_size (result=%ju, rc=%zd)", *res, rc)); + return rc; +} + +static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf, + struct fdisk_parttype *cur) +{ + struct cfdisk_menuitem *d, *cm; + size_t i = 0, nitems, idx = 0; + struct fdisk_parttype *t = NULL; + int has_typestr = 0; + + DBG(FRONTEND, dbgprint("ui: asking for parttype.")); + + /* create cfdisk menu according to label types, note that the + * last cm[] item has to be empty -- so nitems + 1 */ + nitems = cf->cxt->label->nparttypes; + if (!nitems) + return NULL; + cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem)); + if (!cm) + return NULL; + + has_typestr = cf->cxt->label->parttypes[0].typestr && + *cf->cxt->label->parttypes[0].typestr; + + for (i = 0; i < nitems; i++) { + struct fdisk_parttype *x = &cf->cxt->label->parttypes[i]; + char *name; + + if (!x || !x->name) + continue; + cm[i].userdata = x; + if (!has_typestr) + xasprintf(&name, "%2x %s", x->type, x->name); + else { + name = (char *) x->name; + cm[i].desc = x->typestr; + } + cm[i].name = name; + if (x == cur) + idx = i; + } + + /* make the new menu active */ + menu_push(cf, cm); + cf->menu->vertical = 1; + cf->menu->idx = idx; + menu_set_title(cf->menu, _("Select partition type")); + ui_draw_menu(cf); + refresh(); + + do { + int key = getch(); + + if (ui_menu_move(cf, key) == 0) + continue; + + switch (key) { + case KEY_ENTER: + case '\n': + case '\r': + d = menu_get_menuitem(cf, cf->menu->idx); + if (d) + t = (struct fdisk_parttype *) d->userdata; + goto done; + case 'q': + case 'Q': + goto done; + } + } while (1); + +done: + menu_pop(cf); + if (!has_typestr) { + for (i = 0; i < nitems; i++) + free((char *) cm[i].name); + } + free(cm); + DBG(FRONTEND, dbgprint("ui: get parrtype done [type=%s] ", t ? t->name : NULL)); + return t; +} + +/* prints menu with libfdisk labels and waits for users response */ +static int ui_create_label(struct cfdisk *cf) +{ + struct cfdisk_menuitem *d, *cm; + int rc = 1; + size_t i = 0, nitems; + struct fdisk_label *lb = NULL; + + assert(cf); + + DBG(FRONTEND, dbgprint("ui: asking for new disklabe.")); + + /* create cfdisk menu according to libfdisk labels, note that the + * last cm[] item has to be empty -- so nitems + 1 */ + nitems = fdisk_context_get_nlabels(cf->cxt); + cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem)); + + for (i = 0; i < nitems; i++) { + if (fdisk_context_next_label(cf->cxt, &lb)) + break; + cm[i].name = lb->name; + } + + erase(); + ui_center(LINES - 4, + _("Device does not contain a recognized partition table.")); + ui_center(LINES - 3, + _("Please, select a type to create a new disk label.")); + + /* make the new menu active */ + menu_push(cf, cm); + cf->menu->vertical = 1; + menu_set_title(cf->menu, _("Select label type")); + ui_draw_menu(cf); + refresh(); + + do { + int key = getch(); + if (ui_menu_move(cf, key) == 0) + continue; + switch (key) { + case KEY_ENTER: + case '\n': + case '\r': + d = menu_get_menuitem(cf, cf->menu->idx); + if (d) + rc = fdisk_create_disklabel(cf->cxt, d->name); + goto done; + case 'q': + case 'Q': + goto done; + } + } while (1); + +done: + menu_pop(cf); + free(cm); + DBG(FRONTEND, dbgprint("ui: create label done [rc=%d] ", rc)); + return rc; +} + +static int ui_help(void) +{ + size_t i; + static const char *help[] = { + N_("Help Screen for cfdisk"), + "", + N_("This is cfdisk, a curses based disk partitioning program, which"), + N_("allows you to create, delete and modify partitions on your hard"), + N_("disk drive."), + "", + N_("Copyright (C) 2014 Karel Zak "), + N_("Based on the original cfdisk from Kevin E. Martin & aeb."), + "", + N_("Command Meaning"), + N_("------- -------"), + N_(" b Toggle bootable flag of the current partition"), + N_(" d Delete the current partition"), + N_(" h Print this screen"), + N_(" n Create new partition from free space"), + N_(" q Quit program without writing partition table"), + N_(" t Change the partition type"), + N_(" W Write partition table to disk (must enter upper case W)"), + N_(" Since this might destroy data on the disk, you must"), + N_(" either confirm or deny the write by entering `yes' or"), + N_(" `no'"), + N_("Up Arrow Move cursor to the previous partition"), + N_("Down Arrow Move cursor to the next partition"), + N_("Left Arrow Move cursor to the previous menu item"), + N_("Right Arrow Move cursor to the next menu item"), + + "", + N_("Note: All of the commands can be entered with either upper or lower"), + N_("case letters (except for Writes)."), + "", + N_("Use lsblk(8) or partx(8) to see more details about the device.") + }; + + erase(); + for (i = 0; i < ARRAY_SIZE(help); i++) + mvaddstr(i, 1, _(help[i])); + + ui_info(_("Press a key to continue.")); + getch(); + return 0; +} + +/* TODO: use @sz, now 128bytes */ +static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore, + size_t sz __attribute__((__unused__))) +{ + struct fdisk_partition *pa = get_current_partition(cf); + size_t i = 0; + + if (!pa) + return 0; + if (fdisk_partition_is_freespace(pa)) { + ignore[i++] = 'd'; /* delete */ + ignore[i++] = 't'; /* set type */ + ignore[i++] = 'b'; /* set bootable */ + } else { + ignore[i++] = 'n'; + if (!fdisk_is_disklabel(cf->cxt, DOS) && + !fdisk_is_disklabel(cf->cxt, SGI)) + ignore[i++] = 'b'; + } + return i; +} + + +/* returns: error: < 0, success: 0, quit: 1 */ +static int main_menu_action(struct cfdisk *cf, int key) +{ + size_t n; + int ref = 0, rc; + const char *info = NULL, *warn = NULL; + struct fdisk_partition *pa; + + assert(cf); + assert(cf->cxt); + assert(cf->menu); + + if (key == 0) { + struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx); + if (!d) + return 0; + key = d->key; + + } else if (key != 'w' && key != 'W') + key = tolower(key); /* case insensitive except 'W'rite */ + + DBG(FRONTEND, dbgprint("ui: main menu action: key=%c", key)); + + if (cf->menu->ignore && strchr(cf->menu->ignore, key)) { + DBG(FRONTEND, dbgprint(" ignore '%c'", key)); + return 0; + } + + pa = get_current_partition(cf); + n = fdisk_partition_get_partno(pa); + + DBG(FRONTEND, dbgprint("menu action on %p", pa)); + ui_clean_hint(); + ui_clean_info(); + + switch (key) { + case 'b': /* Bootable flag */ + { + int fl = fdisk_is_disklabel(cf->cxt, DOS) ? DOS_FLAG_ACTIVE : + fdisk_is_disklabel(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0; + + if (fl && fdisk_partition_toggle_flag(cf->cxt, n, fl)) + warn = _("Could not toggle the flag."); + else if (fl) + ref = 1; + break; + } + case KEY_DC: + case 'd': /* Delete */ + if (fdisk_delete_partition(cf->cxt, n) != 0) + warn = _("Could not delete partition %zu."); + else + info = _("Partition %zu has been deleted."); + ref = 1; + break; + case 'h': /* help */ + ui_help(); + ref = 1; + break; + case 'n': /* New */ + { + uint64_t start, size, dflt_size; + struct fdisk_partition *npa; /* the new partition */ + + if (!pa || !fdisk_partition_is_freespace(pa)) + return -EINVAL; + npa = fdisk_new_partition(); + if (!npa) + return -ENOMEM; + /* free space range */ + start = fdisk_partition_get_start(pa); + size = dflt_size = fdisk_partition_get_size(pa) * cf->cxt->sector_size; + + if (ui_get_size(cf, _("Partition size: "), &size, 1, size) + == -CFDISK_ERR_ESC) + break; + + if (dflt_size == size) /* default is to fillin all free space */ + fdisk_partition_end_follow_default(npa, 1); + else /* set relative size of the partition */ + fdisk_partition_set_size(npa, size / cf->cxt->sector_size); + + fdisk_partition_set_start(npa, start); + fdisk_partition_partno_follow_default(npa, 1); + /* add to disk label -- libfdisk will ask for missing details */ + rc = fdisk_add_partition(cf->cxt, npa); + fdisk_unref_partition(npa); + if (rc == 0) + ref = 1; + break; + } + case 'q': /* Quit */ + return 1; + case 't': /* Type */ + { + struct fdisk_parttype *t; + + if (!pa || fdisk_partition_is_freespace(pa)) + return -EINVAL; + t = (struct fdisk_parttype *) fdisk_partition_get_type(pa); + t = ui_get_parttype(cf, t); + ref = 1; + + if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0) + info = _("Changed type of the partition %zu."); + else + info = _("Type of the partition %zu is unchanged."); + break; + } + case 'W': /* Write */ + { + char buf[64] = { 0 }; + int rc = ui_get_string(cf, + _("Are you sure you want to write the partition " + "table to disk? "), + _("Type \"yes\" or \"no\" or press ESC to left dialog."), + buf, sizeof(buf)); + + ref = 1; + if (rc <= 0 || strcasecmp(buf, "yes") != 0 + || strcasecmp(buf, _("yes")) != 0) { + info = _("Did not write partition table to disk"); + break; + } + rc = fdisk_write_disklabel(cf->cxt); + if (rc) + warn = _("Failed to write disklabel"); + else { + fdisk_reread_partition_table(cf->cxt); + info = _("The partition table has been altered."); + } + break; + } + default: + break; + } + + if (ref) { + lines_refresh(cf); + ui_refresh(cf); + } + + ui_clean_hint(); + if (warn) + ui_warnx(warn, n + 1); + else if (info) + ui_info(info, n + 1); + + return 0; +} + +static int ui_run(struct cfdisk *cf) +{ + int rc = 0; + + DBG(FRONTEND, dbgprint("ui: start COLS=%d, LINES=%d", COLS, LINES)); + + if (!fdisk_dev_has_disklabel(cf->cxt)) { + rc = ui_create_label(cf); + if (rc < 0) + ui_errx(EXIT_FAILURE, + _("failed to create a new disklabel")); + if (rc) + return rc; + } + + cols_init(cf); + rc = lines_refresh(cf); + if (rc) + ui_errx(EXIT_FAILURE, _("failed to read partitions")); + + menu_push(cf, main_menuitems); + cf->menu->ignore_cb = main_menu_ignore_keys; + + rc = ui_refresh(cf); + if (rc) + return rc; + + do { + int rc = 0, key = getch(); + + if (ui_menu_move(cf, key) == 0) + continue; + + DBG(FRONTEND, dbgprint("ui: main action key >%c<.", key)); + + switch (key) { + case KEY_DOWN: + case '\016': /* ^N */ + case 'j': /* Vi-like alternative */ + ui_table_goto(cf, cf->lines_idx + 1); + break; + case KEY_UP: + case '\020': /* ^P */ + case 'k': /* Vi-like alternative */ + ui_table_goto(cf, (int) cf->lines_idx - 1); + break; + case KEY_PPAGE: + if (cf->page_sz) { + ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz); + break; + } + case KEY_HOME: + ui_table_goto(cf, 0); + break; + case KEY_NPAGE: + if (cf->page_sz) { + ui_table_goto(cf, cf->lines_idx + cf->page_sz); + break; + } + case KEY_END: + ui_table_goto(cf, (int) cf->nlines - 1); + break; + case KEY_ENTER: + case '\n': + case '\r': + rc = main_menu_action(cf, 0); + break; + default: + rc = main_menu_action(cf, key); + if (rc < 0) + beep(); + break; + } + + if (rc == 1) + break; /* quit */ + } while (1); + + menu_pop(cf); + + DBG(FRONTEND, dbgprint("ui: end")); + + return 0; +} + +static void __attribute__ ((__noreturn__)) usage(FILE *out) +{ + fputs(USAGE_HEADER, out); + + fprintf(out, + _(" %1$s [options] \n"), program_invocation_short_name); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -L --color[=] colorize output (auto, always or never)\n"), out); + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); + fputs(USAGE_VERSION, out); + + fprintf(out, USAGE_MAN_TAIL("cfdisk(8)")); + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + int rc, c, colormode = UL_COLORMODE_AUTO; + struct cfdisk _cf = { .lines_idx = 0 }, + *cf = &_cf; + + static const struct option longopts[] = { + { "color", optional_argument, NULL, 'L' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, 0, 0 }, + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + while((c = getopt_long(argc, argv, "L::hV", longopts, NULL)) != -1) { + switch(c) { + case 'h': + usage(stdout); + break; + case 'L': + colormode = UL_COLORMODE_AUTO; + if (optarg) + colormode = colormode_or_err(optarg, + _("unsupported color mode")); + break; + case 'V': + printf(_("%s from %s\n"), program_invocation_short_name, + PACKAGE_STRING); + return EXIT_SUCCESS; + } + } + + colors_init(colormode); + + fdisk_init_debug(0); + cf->cxt = fdisk_new_context(); + if (!cf->cxt) + err(EXIT_FAILURE, _("failed to allocate libfdisk context")); + + fdisk_context_set_ask(cf->cxt, ask_callback, (void *) cf); + + if (optind == argc) + usage(stderr); + + if (fdisk_context_assign_device(cf->cxt, argv[optind], 0) != 0) + err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); + + /* Don't use err(), warn() from this point */ + ui_init(cf); + ui_run(cf); + ui_end(); + + free(cf->lines); + free(cf->linesbuf); + fdisk_unref_table(cf->table); + + rc = fdisk_context_deassign_device(cf->cxt); + fdisk_free_context(cf->cxt); + DBG(FRONTEND, dbgprint("bye! [rc=%d]", rc)); + return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/disk-utils/fdisk-menu.c b/disk-utils/fdisk-menu.c new file mode 100644 index 000000000..d7b852deb --- /dev/null +++ b/disk-utils/fdisk-menu.c @@ -0,0 +1,880 @@ + +#include +#include +#include +#include +#include + +#include "c.h" +#include "fdisk.h" +#include "pt-sun.h" +#include "pt-mbr.h" + +struct menu_entry { + const char key; /* command key */ + const char *title; /* help string */ + unsigned int normal : 1, /* normal mode */ + expert : 1, /* expert mode */ + hidden : 1; /* be sensitive for this key, + but don't print it in help */ + + enum fdisk_labeltype label; /* only for this label */ + enum fdisk_labeltype exclude; /* all labels except this */ + enum fdisk_labeltype parent; /* for nested PT */ +}; + +#define IS_MENU_SEP(e) ((e)->key == '-') +#define IS_MENU_HID(e) ((e)->hidden) + +struct menu { + enum fdisk_labeltype label; /* only for this label */ + enum fdisk_labeltype exclude; /* all labels except this */ + + unsigned int nonested : 1; /* don't make this menu active in nested PT */ + + int (*callback)(struct fdisk_context **, + const struct menu *, + const struct menu_entry *); + + struct menu_entry entries[]; /* NULL terminated array */ +}; + +struct menu_context { + size_t menu_idx; /* the current menu */ + size_t entry_idx; /* index with in the current menu */ +}; + +#define MENU_CXT_EMPTY { 0, 0 } +#define DECLARE_MENU_CB(x) \ + static int x(struct fdisk_context **, \ + const struct menu *, \ + const struct menu_entry *) + +DECLARE_MENU_CB(gpt_menu_cb); +DECLARE_MENU_CB(sun_menu_cb); +DECLARE_MENU_CB(sgi_menu_cb); +DECLARE_MENU_CB(geo_menu_cb); +DECLARE_MENU_CB(dos_menu_cb); +DECLARE_MENU_CB(bsd_menu_cb); +DECLARE_MENU_CB(createlabel_menu_cb); +DECLARE_MENU_CB(generic_menu_cb); + +/* + * Menu entry macros: + * MENU_X* expert mode only + * MENU_B* both -- expert + normal mode + * + * *_E exclude this label + * *_H hidden + * *_L only for this label + */ + +/* separator */ +#define MENU_SEP(t) { .title = t, .key = '-', .normal = 1 } +#define MENU_XSEP(t) { .title = t, .key = '-', .expert = 1 } +#define MENU_BSEP(t) { .title = t, .key = '-', .expert = 1, .normal = 1 } + +/* entry */ +#define MENU_ENT(k, t) { .title = t, .key = k, .normal = 1 } +#define MENU_ENT_E(k, t, l) { .title = t, .key = k, .normal = 1, .exclude = l } +#define MENU_ENT_L(k, t, l) { .title = t, .key = k, .normal = 1, .label = l } + +#define MENU_XENT(k, t) { .title = t, .key = k, .expert = 1 } +#define MENU_XENT_H(k, t) { .title = t, .key = k, .expert = 1, .hidden = 1 } + +#define MENU_BENT(k, t) { .title = t, .key = k, .expert = 1, .normal = 1 } +#define MENU_BENT_E(k, t, l) { .title = t, .key = k, .expert = 1, .normal = 1, .exclude = l } + +#define MENU_ENT_NEST(k, t, l, p) { .title = t, .key = k, .normal = 1, .label = l, .parent = p } +#define MENU_XENT_NEST(k, t, l, p) { .title = t, .key = k, .expert = 1, .label = l, .parent = p } + +/* Generic menu */ +struct menu menu_generic = { + .callback = generic_menu_cb, + .entries = { + MENU_BSEP(N_("Generic")), + MENU_ENT ('d', N_("delete a partition")), + MENU_ENT ('l', N_("list known partition types")), + MENU_ENT ('n', N_("add a new partition")), + MENU_BENT ('p', N_("print the partition table")), + MENU_ENT ('t', N_("change a partition type")), + MENU_BENT_E('v', N_("verify the partition table"), FDISK_DISKLABEL_BSD), + + MENU_XENT('d', N_("print the raw data of the first sector from the device")), + MENU_XENT('D', N_("print the raw data of the disklabel from the device")), + + MENU_SEP(N_("Misc")), + MENU_BENT ('m', N_("print this menu")), + MENU_ENT_E('u', N_("change display/entry units"), FDISK_DISKLABEL_GPT), + MENU_ENT_E('x', N_("extra functionality (experts only)"), FDISK_DISKLABEL_BSD), + + MENU_BSEP(N_("Save & Exit")), + MENU_ENT_E('w', N_("write table to disk and exit"), FDISK_DISKLABEL_BSD), + MENU_ENT_L('w', N_("write table to disk"), FDISK_DISKLABEL_BSD), + MENU_BENT ('q', N_("quit without saving changes")), + MENU_XENT ('r', N_("return to main menu")), + + MENU_ENT_NEST('r', N_("return from BSD to DOS"), FDISK_DISKLABEL_BSD, FDISK_DISKLABEL_DOS), + + { 0, NULL } + } +}; + +struct menu menu_createlabel = { + .callback = createlabel_menu_cb, + .exclude = FDISK_DISKLABEL_BSD, + .nonested = 1, + .entries = { + MENU_SEP(N_("Create a new label")), + MENU_ENT('g', N_("create a new empty GPT partition table")), + MENU_ENT('G', N_("create a new empty SGI (IRIX) partition table")), + MENU_ENT('o', N_("create a new empty DOS partition table")), + MENU_ENT('s', N_("create a new empty Sun partition table")), + + /* backward compatibility -- be sensitive to 'g', but don't + * print it in the expert menu */ + MENU_XENT_H('g', N_("create an IRIX (SGI) partition table")), + { 0, NULL } + } +}; + +struct menu menu_geo = { + .callback = geo_menu_cb, + .exclude = FDISK_DISKLABEL_GPT | FDISK_DISKLABEL_BSD, + .entries = { + MENU_XSEP(N_("Geometry")), + MENU_XENT('c', N_("change number of cylinders")), + MENU_XENT('h', N_("change number of heads")), + MENU_XENT('s', N_("change number of sectors/track")), + { 0, NULL } + } +}; + +struct menu menu_gpt = { + .callback = gpt_menu_cb, + .label = FDISK_DISKLABEL_GPT, + .entries = { + MENU_XSEP(N_("GPT")), + MENU_XENT('i', N_("change disk GUID")), + MENU_XENT('n', N_("change partition name")), + MENU_XENT('u', N_("change partition UUID")), + MENU_XENT('M', N_("enter protective/hybrid MBR")), + + MENU_XSEP(""), + MENU_XENT('A', N_("toggle the legacy BIOS bootable flag")), + MENU_XENT('B', N_("toggle the no block IO protocol flag")), + MENU_XENT('R', N_("toggle the required partition flag")), + MENU_XENT('S', N_("toggle the GUID specific bits")), + + { 0, NULL } + } +}; + +struct menu menu_sun = { + .callback = sun_menu_cb, + .label = FDISK_DISKLABEL_SUN, + .entries = { + MENU_BSEP(N_("Sun")), + MENU_ENT('a', N_("toggle the read-only flag")), + MENU_ENT('c', N_("toggle the mountable flag")), + + MENU_XENT('a', N_("change number of alternate cylinders")), + MENU_XENT('e', N_("change number of extra sectors per cylinder")), + MENU_XENT('i', N_("change interleave factor")), + MENU_XENT('o', N_("change rotation speed (rpm)")), + MENU_XENT('y', N_("change number of physical cylinders")), + { 0, NULL } + } +}; + +struct menu menu_sgi = { + .callback = sgi_menu_cb, + .label = FDISK_DISKLABEL_SGI, + .entries = { + MENU_SEP(N_("SGI")), + MENU_ENT('a', N_("select bootable partition")), + MENU_ENT('b', N_("edit bootfile entry")), + MENU_ENT('c', N_("select sgi swap partition")), + MENU_ENT('i', N_("create SGI info")), + { 0, NULL } + } +}; + +struct menu menu_dos = { + .callback = dos_menu_cb, + .label = FDISK_DISKLABEL_DOS, + .entries = { + MENU_BSEP(N_("DOS (MBR)")), + MENU_ENT('a', N_("toggle a bootable flag")), + MENU_ENT('b', N_("edit nested BSD disklabel")), + MENU_ENT('c', N_("toggle the dos compatibility flag")), + + MENU_XENT('b', N_("move beginning of data in a partition")), + MENU_XENT('f', N_("fix partition order")), + MENU_XENT('i', N_("change the disk identifier")), + + MENU_XENT_NEST('M', N_("return from protective/hybrid MBR to GPT"), + FDISK_DISKLABEL_DOS, FDISK_DISKLABEL_GPT), + { 0, NULL } + } +}; + +struct menu menu_bsd = { + .callback = bsd_menu_cb, + .label = FDISK_DISKLABEL_BSD, + .entries = { + MENU_SEP(N_("BSD")), + MENU_ENT('e', N_("edit drive data")), + MENU_ENT('i', N_("install bootstrap")), + MENU_ENT('s', N_("show complete disklabel")), + MENU_ENT('x', N_("link BSD partition to non-BSD partition")), + { 0, NULL } + } +}; + +static const struct menu *menus[] = { + &menu_gpt, + &menu_sun, + &menu_sgi, + &menu_dos, + &menu_bsd, + &menu_geo, + &menu_generic, + &menu_createlabel, +}; + +static const struct menu_entry *next_menu_entry( + struct fdisk_context *cxt, + struct menu_context *mc) +{ + while (mc->menu_idx < ARRAY_SIZE(menus)) { + const struct menu *m = menus[mc->menu_idx]; + const struct menu_entry *e = &(m->entries[mc->entry_idx]); + + /* + * whole-menu filter + */ + + /* no more entries */ + if (e->title == NULL || + /* menu wanted for specified labels only */ + (m->label && cxt->label && !(m->label & cxt->label->id)) || + /* unwanted for nested PT */ + (m->nonested && cxt->parent) || + /* menu excluded for specified labels */ + (m->exclude && cxt->label && (m->exclude & cxt->label->id))) { + mc->menu_idx++; + mc->entry_idx = 0; + continue; + } + + /* + * per entry filter + */ + + /* excluded for the current label */ + if ((e->exclude && cxt->label && e->exclude & cxt->label->id) || + /* entry wanted for specified labels only */ + (e->label && cxt->label && !(e->label & cxt->label->id)) || + /* exclude non-expert entries in expect mode */ + (e->expert == 0 && fdisk_context_display_details(cxt)) || + /* nested only */ + (e->parent && (!cxt->parent || cxt->parent->label->id != e->parent)) || + /* exclude non-normal entries in normal mode */ + (e->normal == 0 && !fdisk_context_display_details(cxt))) { + + mc->entry_idx++; + continue; + } + mc->entry_idx++; + return e; + + } + return NULL; +} + +/* returns @menu and menu entry for then @key */ +static const struct menu_entry *get_fdisk_menu_entry( + struct fdisk_context *cxt, + int key, + const struct menu **menu) +{ + struct menu_context mc = MENU_CXT_EMPTY; + const struct menu_entry *e; + + while ((e = next_menu_entry(cxt, &mc))) { + if (IS_MENU_SEP(e) || e->key != key) + continue; + + if (menu) + *menu = menus[mc.menu_idx]; + return e; + } + + return NULL; +} + +static int menu_detect_collisions(struct fdisk_context *cxt) +{ + struct menu_context mc = MENU_CXT_EMPTY; + const struct menu_entry *e, *r; + + while ((e = next_menu_entry(cxt, &mc))) { + if (IS_MENU_SEP(e)) + continue; + + r = get_fdisk_menu_entry(cxt, e->key, NULL); + if (!r) { + DBG(FRONTEND, dbgprint("warning: not found " + "entry for %c", e->key)); + return -1; + } + if (r != e) { + DBG(FRONTEND, dbgprint("warning: duplicate key '%c'", + e->key)); + DBG(FRONTEND, dbgprint(" : %s", e->title)); + DBG(FRONTEND, dbgprint(" : %s", r->title)); + abort(); + } + } + + return 0; +} + +static int print_fdisk_menu(struct fdisk_context *cxt) +{ + struct menu_context mc = MENU_CXT_EMPTY; + const struct menu_entry *e; + + ON_DBG(FRONTEND, menu_detect_collisions(cxt)); + + if (fdisk_context_display_details(cxt)) + printf(_("\nHelp (expert commands):\n")); + else + printf(_("\nHelp:\n")); + + while ((e = next_menu_entry(cxt, &mc))) { + if (IS_MENU_HID(e)) + continue; /* hidden entry */ + if (IS_MENU_SEP(e) && (!e->title || !*e->title)) + printf("\n"); + else if (IS_MENU_SEP(e)) { + color_enable(UL_COLOR_BOLD); + printf("\n %s\n", _(e->title)); + color_disable(); + } else + printf(" %c %s\n", e->key, _(e->title)); + } + fputc('\n', stdout); + + if (cxt->parent) + fdisk_info(cxt, _("You're editing nested '%s' partition table, " + "primary partition table is '%s'."), + cxt->label->name, + cxt->parent->label->name); + + return 0; +} + +/* Asks for command, verify the key and perform the command or + * returns the command key if no callback for the command is + * implemented. + * + * Note that this function might exchange the context pointer to + * switch to another (nested) context. + * + * Returns: <0 on error + * 0 on success (the command performed) + * >0 if no callback (then returns the key) + */ +int process_fdisk_menu(struct fdisk_context **cxt0) +{ + struct fdisk_context *cxt = *cxt0; + const struct menu_entry *ent; + const struct menu *menu; + int key, rc; + const char *prompt; + char buf[BUFSIZ]; + + if (fdisk_context_display_details(cxt)) + prompt = _("Expert command (m for help): "); + else + prompt = _("Command (m for help): "); + + fputc('\n',stdout); + rc = get_user_reply(cxt, prompt, buf, sizeof(buf)); + if (rc) + return rc; + + key = buf[0]; + ent = get_fdisk_menu_entry(cxt, key, &menu); + if (!ent) { + fdisk_warnx(cxt, _("%c: unknown command"), key); + return -EINVAL; + } + + rc = 0; + DBG(FRONTEND, dbgprint("selected: key=%c, entry='%s'", + key, ent->title)); + + /* menu has implemented callback, use it */ + if (menu->callback) + rc = menu->callback(cxt0, menu, ent); + else { + DBG(FRONTEND, dbgprint("no callback for key '%c'", key)); + rc = -EINVAL; + } + + DBG(FRONTEND, dbgprint("process menu done [rc=%d]", rc)); + return rc; +} + +/* + * Basic fdisk actions + */ +static int generic_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + int rc = 0; + size_t n; + + /* actions shared between expert and normal mode */ + switch (ent->key) { + case 'p': + list_disk_geometry(cxt); + list_disklabel(cxt); + break; + case 'w': + rc = fdisk_write_disklabel(cxt); + if (rc) + err(EXIT_FAILURE, _("failed to write disklabel")); + if (cxt->parent) + break; /* nested PT, don't leave */ + fdisk_info(cxt, _("The partition table has been altered.")); + rc = fdisk_reread_partition_table(cxt); + if (!rc) + rc = fdisk_context_deassign_device(cxt); + /* fallthrough */ + case 'q': + fdisk_free_context(cxt); + fputc('\n', stdout); + exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE); + case 'm': + rc = print_fdisk_menu(cxt); + break; + case 'v': + rc = fdisk_verify_disklabel(cxt); + break; + } + + /* expert mode */ + if (ent->expert) { + switch (ent->key) { + case 'd': + dump_firstsector(cxt); + break; + case 'D': + dump_disklabel(cxt); + break; + case 'r': + rc = fdisk_context_enable_details(cxt, 0); + break; + } + return rc; + } + + /* normal mode */ + switch (ent->key) { + case 'd': + rc = fdisk_ask_partnum(cxt, &n, FALSE); + if (!rc) + rc = fdisk_delete_partition(cxt, n); + if (rc) + fdisk_warnx(cxt, _("Could not delete partition %zu"), n + 1); + else + fdisk_info(cxt, _("Partition %zu has been deleted."), n + 1); + break; + case 'l': + list_partition_types(cxt); + break; + case 'n': + rc = fdisk_add_partition(cxt, NULL); + break; + case 't': + change_partition_type(cxt); + break; + case 'u': + fdisk_context_set_unit(cxt, + fdisk_context_use_cylinders(cxt) ? "sectors" : + "cylinders"); + if (fdisk_context_use_cylinders(cxt)) + fdisk_info(cxt, _("Changing display/entry units to cylinders (DEPRECATED!).")); + else + fdisk_info(cxt, _("Changing display/entry units to sectors.")); + break; + case 'x': + fdisk_context_enable_details(cxt, 1); + break; + case 'r': + /* return from nested BSD to DOS */ + if (cxt->parent) { + *cxt0 = cxt->parent; + + fdisk_info(cxt, _("Leaving nested disklabel.")); + fdisk_free_context(cxt); + cxt = *cxt0; + } + break; + } + + return rc; +} + + +/* + * This is fdisk frontend for GPT specific libfdisk functions that + * are not expported by generic libfdisk API. + */ +static int gpt_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + struct fdisk_context *mbr; + size_t n; + int rc = 0; + + assert(cxt); + assert(ent); + assert(fdisk_is_disklabel(cxt, GPT)); + + DBG(FRONTEND, dbgprint("enter GPT menu")); + + if (ent->expert) { + switch (ent->key) { + case 'i': + return fdisk_set_disklabel_id(cxt); + case 'M': + mbr = fdisk_new_nested_context(cxt, "dos"); + if (!mbr) + return -ENOMEM; + *cxt0 = cxt = mbr; + fdisk_context_enable_details(cxt, 1); /* keep us in expert mode */ + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Entering protective/hybrid MBR disklabel.")); + return 0; + } + + /* actions where is necessary partnum */ + rc = fdisk_ask_partnum(cxt, &n, FALSE); + if (rc) + return rc; + + switch(ent->key) { + case 'u': + rc = fdisk_gpt_partition_set_uuid(cxt, n); + break; + case 'n': + rc = fdisk_gpt_partition_set_name(cxt, n); + break; + case 'A': + rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_LEGACYBOOT); + break; + case 'B': + rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_NOBLOCK); + break; + case 'R': + rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_REQUIRED); + break; + case 'S': + rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC); + break; + } + } + return rc; +} + + +/* + * This is fdisk frontend for MBR specific libfdisk functions that + * are not expported by generic libfdisk API. + */ +static int dos_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + int rc = 0; + + DBG(FRONTEND, dbgprint("enter DOS menu")); + + if (!ent->expert) { + switch (ent->key) { + case 'a': + { + size_t n; + rc = fdisk_ask_partnum(cxt, &n, FALSE); + if (!rc) + rc = fdisk_partition_toggle_flag(cxt, n, DOS_FLAG_ACTIVE); + break; + } + case 'b': + { + struct fdisk_context *bsd + = fdisk_new_nested_context(cxt, "bsd"); + if (!bsd) + return -ENOMEM; + if (!fdisk_dev_has_disklabel(bsd)) + rc = fdisk_create_disklabel(bsd, "bsd"); + if (rc) + fdisk_free_context(bsd); + else { + *cxt0 = cxt = bsd; + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Entering nested BSD disklabel.")); + } + break; + } + case 'c': + toggle_dos_compatibility_flag(cxt); + break; + } + return rc; + } + + /* expert mode */ + switch (ent->key) { + case 'b': + { + size_t n; + rc = fdisk_ask_partnum(cxt, &n, FALSE); + if (!rc) + rc = fdisk_dos_move_begin(cxt, n); + break; + } + case 'f': + rc = fdisk_dos_fix_order(cxt); + break; + case 'i': + rc = fdisk_set_disklabel_id(cxt); + break; + case 'M': + /* return from nested MBR to GPT */ + if (cxt->parent) { + *cxt0 = cxt->parent; + + fdisk_info(cxt, _("Leaving nested disklabel.")); + fdisk_free_context(cxt); + cxt = *cxt0; + } + break; + } + return rc; +} + +static int sun_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + int rc = 0; + + DBG(FRONTEND, dbgprint("enter SUN menu")); + + assert(cxt); + assert(ent); + assert(fdisk_is_disklabel(cxt, SUN)); + + DBG(FRONTEND, dbgprint("enter SUN menu")); + + /* normal mode */ + if (!ent->expert) { + size_t n; + + rc = fdisk_ask_partnum(cxt, &n, FALSE); + if (rc) + return rc; + switch (ent->key) { + case 'a': + rc = fdisk_partition_toggle_flag(cxt, n, SUN_FLAG_RONLY); + break; + case 'c': + rc = fdisk_partition_toggle_flag(cxt, n, SUN_FLAG_UNMNT); + break; + } + return rc; + } + + /* expert mode */ + switch (ent->key) { + case 'a': + rc = fdisk_sun_set_alt_cyl(cxt); + break; + case 'e': + rc = fdisk_sun_set_xcyl(cxt); + break; + case 'i': + rc = fdisk_sun_set_ilfact(cxt); + break; + case 'o': + rc = fdisk_sun_set_rspeed(cxt); + break; + case 'y': + rc = fdisk_sun_set_pcylcount(cxt); + break; + } + return rc; +} + +static int sgi_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + int rc = -EINVAL; + size_t n = 0; + + DBG(FRONTEND, dbgprint("enter SGI menu")); + + assert(cxt); + assert(ent); + assert(fdisk_is_disklabel(cxt, SGI)); + + if (ent->expert) + return rc; + + switch (ent->key) { + case 'a': + rc = fdisk_ask_partnum(cxt, &n, FALSE); + if (!rc) + rc = fdisk_partition_toggle_flag(cxt, n, SGI_FLAG_BOOT); + break; + case 'b': + fdisk_sgi_set_bootfile(cxt); + break; + case 'c': + rc = fdisk_ask_partnum(cxt, &n, FALSE); + if (!rc) + rc = fdisk_partition_toggle_flag(cxt, n, SGI_FLAG_SWAP); + break; + case 'i': + rc = fdisk_sgi_create_info(cxt); + break; + } + + return rc; +} + +/* + * This is fdisk frontend for BSD specific libfdisk functions that + * are not expported by generic libfdisk API. + */ +static int bsd_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + int rc = 0, org; + + assert(cxt); + assert(ent); + assert(fdisk_is_disklabel(cxt, BSD)); + + DBG(FRONTEND, dbgprint("enter BSD menu")); + + switch(ent->key) { + case 'e': + rc = fdisk_bsd_edit_disklabel(cxt); + break; + case 'i': + rc = fdisk_bsd_write_bootstrap(cxt); + break; + case 's': + org = fdisk_context_display_details(cxt); + + fdisk_context_enable_details(cxt, 1); + list_disklabel(cxt); + fdisk_context_enable_details(cxt, org); + break; + case 'x': + rc = fdisk_bsd_link_partition(cxt); + break; + } + return rc; +} + +/* C/H/S commands */ +static int geo_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + int rc = -EINVAL; + uintmax_t c = 0, h = 0, s = 0; + + DBG(FRONTEND, dbgprint("enter GEO menu")); + + assert(cxt); + assert(ent); + + switch (ent->key) { + case 'c': + rc = fdisk_ask_number(cxt, 1, cxt->geom.cylinders, + 1048576, _("Number of cylinders"), &c); + break; + case 'h': + rc = fdisk_ask_number(cxt, 1, cxt->geom.heads, + 256, _("Number of heads"), &h); + break; + case 's': + rc = fdisk_ask_number(cxt, 1, cxt->geom.sectors, + 63, _("Number of sectors"), &s); + break; + } + + if (!rc) + fdisk_override_geometry(cxt, c, h, s); + return rc; +} + +static int createlabel_menu_cb(struct fdisk_context **cxt0, + const struct menu *menu __attribute__((__unused__)), + const struct menu_entry *ent) +{ + struct fdisk_context *cxt = *cxt0; + int rc = -EINVAL; + + DBG(FRONTEND, dbgprint("enter Create label menu")); + + assert(cxt); + assert(ent); + + if (ent->expert) { + switch (ent->key) { + case 'g': + /* Deprecated, use 'G' in main menu, just for backward + * compatibility only. */ + rc = fdisk_create_disklabel(cxt, "sgi"); + break; + } + return rc; + } + + switch (ent->key) { + case 'g': + fdisk_create_disklabel(cxt, "gpt"); + break; + case 'G': + fdisk_create_disklabel(cxt, "sgi"); + break; + case 'o': + fdisk_create_disklabel(cxt, "dos"); + break; + case 's': + fdisk_create_disklabel(cxt, "sun"); + break; + } + return rc; +} diff --git a/disk-utils/fdisk.8 b/disk-utils/fdisk.8 new file mode 100644 index 000000000..0a3c1e706 --- /dev/null +++ b/disk-utils/fdisk.8 @@ -0,0 +1,272 @@ +.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl) +.\" Copyright 2012 Davidlohr Bueso +.\" Copyright (C) 2013 Karel Zak +.\" May be distributed under the GNU General Public License +.TH FDISK 8 "September 2013" "util-linux" "System Administration" + +.SH NAME +fdisk \- manipulate disk partition table + +.SH SYNOPSIS +.B fdisk +.RB [ options ] +.I device +.sp +.B fdisk \-l +.RI [ device ...] + +.SH DESCRIPTION +.B fdisk +is a dialog-driven program for creation and manipulation of partition tables. +It understands GPT, MBR, Sun, SGI and BSD partition tables. + +Block devices can be divided into one or more logical disks called +.IR partitions . +This division is recorded in the +.IR "partition table" , +usually found in sector 0 of the disk. +(In the BSD world one talks about `disk slices' and a `disklabel'.) + +All partitioning is driven by device I/O limits (the topology) by default. +.B fdisk +is able to optimize the disk layout for a 4K-sector size and use an alignment offset on +modern devices for MBR and GPT. It is always a good idea to follow \fBfdisk\fR's defaults +as the default values (e.g. first and last partition sectors) and partition +sizes specified by the +{M,G,...} notation are always aligned according +to the device properties. + +Note that +.BR partx (8) +provides a rich interface for scripts to print disk layouts, +.B fdisk +is mostly designed for humans. Backward compatibility in the output of +.B fdisk +is not guaranteed. The input (the commands) should always be backward compatible. + +.SH OPTIONS +.TP +.BI "\-b " sectorsize +Specify the sector size of the disk. Valid values are 512, 1024, 2048, and 4096. +(Recent kernels know the sector size. Use this option only on old kernels or +to override the kernel's ideas.) Since util-linux-2.17, \fBfdisk\fR differentiates +between logical and physical sector size. This option changes both sector sizes to +.IB sectorsize . +.TP +.BR "\-c"[=\fImode\fR] +Specify the compatibility mode, 'dos' or 'nondos'. The default is non-DOS +mode. For backward compatibility, it is possible to use the option without +the \fImode\fR argument -- then the default is used. Note that the optional +\fImode\fR argument cannot be separated from the \fB-c\fR option by a space, +the correct form is for example '-c=dos'. This option is DEPRECATED. +.TP +.BI "\-C " cylinders +Specify the number of cylinders of the disk. +I have no idea why anybody would want to do so. This option is DEPRECATED. +.TP +.BI "\-H " heads +Specify the number of heads of the disk. (Not the physical number, +of course, but the number used for partition tables.) +Reasonable values are 255 and 16. This option is DEPRECATED. +.TP +.BI "\-S " sectors +Specify the number of sectors per track of the disk. +(Not the physical number, of course, but the number used for +partition tables.) +A reasonable value is 63. This option is DEPRECATED. +.TP +.BI \-h +Display a help text and exit. +.TP +.BR "\-L"[=\fIwhen\fR] +Colorize the output in interactive mode. The optional argument \fIwhen\fP can +be \fBauto\fR, \fBnever\fR or \fBalways\fR. The default is \fBauto\fR. +.TP +.B \-l +List the partition tables for the specified devices and then exit. +If no devices are given, those mentioned in +.I /proc/partitions +(if that file exists) are used. +.TP +.BI "\-s " partition... +Print the size (in blocks) of each given partition. This option is DEPRECATED +in favour of +.BR blockdev (1). +.TP +.BI "\-t " type +Enable support only for disklabels of the specified \fItype\fP, and disable +support for all other types. +This is necessary for example to access a protective or hybrid MBR on devices +with GPT. +.TP +.BR "\-u"[=\fIunit\fR] +When listing partition tables, show sizes in 'sectors' or in 'cylinders'. The +default is to show sizes in sectors. For backward compatibility, it is possible +to use the option without the \fIunit\fR argument -- then the default is used. +Note that the optional \fIunit\fR argument cannot be separated from the \fB-u\fR +option by a space, the correct form is for example '-u=cylinders'. +.TP +.B \-v +Display version information and exit. + +.SH DEVICES +The +.I device +is usually /dev/sda, /dev/sdb or so. A device name refers to the entire disk. +Old systems without libata (a library used inside the Linux kernel to support +ATA host controllers and devices) make a difference between IDE and SCSI disks. +In such cases the device name will be /dev/hd* (IDE) or /dev/sd* (SCSI). + +The +.I partition +is a device name followed by a partition number. For example, /dev/sda1 is the +first partition on the first hard disk in the system. See also Linux kernel +documentation (the Documentation/devices.txt file). + +.SH DISK LABELS +.B GPT (GUID Partition Table) +.RS +GPT is modern standard for the layout of the partition table. GPT uses 64-bit +logical block addresses, checksums, UUIDs and names for partitions and an +unlimited number of partitions (although the number of partitions is +usually restricted to 128 in many partitioning tools). + +Note that the first sector is still reserved for a +.B protective MBR +in the GPT specification. It prevents MBR-only partitioning tools +from mis-recognizing and overwriting GPT disks. + +GPT is always a better choice than MBR, especially on modern hardware with a UEFI +boot loader. +.RE + +.B DOS-type (MBR) +.RS +A DOS-type partition table can describe an unlimited number of partitions. In sector 0 +there is room for the description of 4 partitions (called `primary'). One of +these may be an extended partition; this is a box holding logical partitions, +with descriptors found in a linked list of sectors, each preceding the +corresponding logical partitions. The four primary partitions, present or not, +get numbers 1-4. Logical partitions are numbered starting from 5. + +In a DOS-type partition table the starting offset and the size of each +partition is stored in two ways: as an absolute number of sectors (given in 32 +bits), and as a +.B Cylinders/Heads/Sectors +triple (given in 10+8+6 bits). The former is OK -- with 512-byte sectors this +will work up to 2 TB. The latter has two problems. First, these C/H/S fields +can be filled only when the number of heads and the number of sectors per track +are known. And second, even if we know what these numbers should be, the 24 +bits that are available do not suffice. DOS uses C/H/S only, Windows uses +both, Linux never uses C/H/S. The +.B C/H/S addressing is deprecated +and may be unsupported in some later fdisk version. + +.B Please, read the DOS-mode section if you want DOS-compatible partitions. +.B fdisk +does not care about cylinder boundaries by default. +.RE + +.B BSD/Sun-type +.RS +A BSD/Sun disklabel can describe 8 partitions, the third of which should be a `whole +disk' partition. Do not start a partition that actually uses its first sector +(like a swap partition) at cylinder 0, since that will destroy the disklabel. +Note that a +.B BSD label +is usually nested within a DOS partition. +.RE + +.B IRIX/SGI-type +.RS +An IRIX/SGI disklabel can describe 16 partitions, the eleventh of which should be an entire +`volume' partition, while the ninth should be labeled `volume header'. The +volume header will also cover the partition table, i.e., it starts at block +zero and extends by default over five cylinders. The remaining space in the +volume header may be used by header directory entries. No partitions may +overlap with the volume header. Also do not change its type or make some +filesystem on it, since you will lose the partition table. Use this type of +label only when working with Linux on IRIX/SGI machines or IRIX/SGI disks under +Linux. +.RE + +A sync() and an ioctl(BLKRRPART) (rereading the partition table from disk) +are performed before exiting when the partition table has been updated. + +.SH "DOS mode and DOS 6.x WARNING" +.B Note that all this is deprecated. You don't have to care about things like +.B geometry and cylinders on modern operating systems. If you really want +.B DOS-compatible partitioning then you have to enable DOS mode and cylinder +.B units by using the '-c=dos -u=cylinders' fdisk command-line options. + +The DOS 6.x FORMAT command looks for some information in the first sector of +the data area of the partition, and treats this information as more reliable +than the information in the partition table. DOS FORMAT expects DOS FDISK to +clear the first 512 bytes of the data area of a partition whenever a size +change occurs. DOS FORMAT will look at this extra information even if the /U +flag is given -- we consider this a bug in DOS FORMAT and DOS FDISK. + +The bottom line is that if you use \fBfdisk\fR or \fBcfdisk\fR to change the +size of a DOS partition table entry, then you must also use +.BR dd "(1) to " "zero the first 512 bytes" +of that partition before using DOS FORMAT to format the partition. For +example, if you were using \fBfdisk\fR to make a DOS partition table entry for +/dev/sda1, then (after exiting \fBfdisk\fR and rebooting Linux so that the +partition table information is valid) you would use the command "dd +if=/dev/zero of=/dev/sda1 bs=512 count=1" to zero the first 512 bytes of the +partition. + +.B fdisk +usually obtains the disk geometry automatically. This is not necessarily the +physical disk geometry (indeed, modern disks do not really have anything like a +physical geometry, certainly not something that can be described in the simplistic +Cylinders/Heads/Sectors form), but it is the disk geometry that MS-DOS uses for +the partition table. + +Usually all goes well by default, and there are no problems if Linux is the +only system on the disk. However, if the disk has to be shared with other +operating systems, it is often a good idea to let an fdisk from another +operating system make at least one partition. When Linux boots it looks at the +partition table, and tries to deduce what (fake) geometry is required for good +cooperation with other systems. + +Whenever a partition table is printed out in DOS mode, a consistency check is +performed on the partition table entries. This check verifies that the +physical and logical start and end points are identical, and that each +partition starts and ends on a cylinder boundary (except for the first +partition). + +Some versions of MS-DOS create a first partition which does not begin +on a cylinder boundary, but on sector 2 of the first cylinder. +Partitions beginning in cylinder 1 cannot begin on a cylinder boundary, but +this is unlikely to cause difficulty unless you have OS/2 on your machine. + +For best results, you should always use an OS-specific partition table +program. For example, you should make DOS partitions with the DOS FDISK +program and Linux partitions with the Linux fdisk or Linux cfdisk programs. + +.SH AUTHORS +.MT kzak@redhat.com +Karel Zak +.ME +.br +.MT dave@gnu.org +Davidlohr Bueso +.ME +.br +.PP +The original version was written by +Andries E. Brouwer, A. V. Le Blanc and others. + +.SH ENVIRONMENT +.IP "Setting LIBFDISK_DEBUG=0xffff enables debug output." + +.SH "SEE ALSO" +.BR cfdisk (8), +.BR sfdisk (8), +.BR mkfs (8), +.BR partx (8) + +.SH AVAILABILITY +The fdisk command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c new file mode 100644 index 000000000..e003baf64 --- /dev/null +++ b/disk-utils/fdisk.c @@ -0,0 +1,943 @@ +/* + * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) + * Copyright (C) 2012 Davidlohr Bueso + * + * Copyright (C) 2007-2013 Karel Zak + * + * 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 1 or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c.h" +#include "xalloc.h" +#include "all-io.h" +#include "nls.h" +#include "rpmatch.h" +#include "blkdev.h" +#include "mbsalign.h" +#include "fdisk.h" +#include "wholedisk.h" +#include "pathnames.h" +#include "canonicalize.h" +#include "strutils.h" +#include "closestream.h" + +#include "pt-sun.h" /* to toggle flags */ + +#ifdef HAVE_LINUX_COMPILER_H +# include +#endif +#ifdef HAVE_LINUX_BLKPG_H +# include +#endif + +static void __attribute__ ((__noreturn__)) usage(FILE *out) +{ + fputs(USAGE_HEADER, out); + + fprintf(out, + _(" %1$s [options] change partition table\n" + " %1$s [options] -l [] list partition table(s)\n"), + program_invocation_short_name); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -b sector size (512, 1024, 2048 or 4096)\n"), out); + fputs(_(" -c[=] compatible mode: 'dos' or 'nondos' (default)\n"), out); + fputs(_(" -h print this help text\n"), out); + fputs(_(" -c[=] compatible mode: 'dos' or 'nondos' (default)\n"), out); + fputs(_(" -L[=] colorize output (auto, always or never)\n"), out); + fputs(_(" -t force fdisk to recognize specified partition table type only\n"), out); + fputs(_(" -u[=] display units: 'cylinders' or 'sectors' (default)\n"), out); + fputs(_(" -v print program version\n"), out); + fputs(_(" -C specify the number of cylinders\n"), out); + fputs(_(" -H specify the number of heads\n"), out); + fputs(_(" -S specify the number of sectors per track\n"), out); + + fprintf(out, USAGE_MAN_TAIL("fdisk(8)")); + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + + + +int get_user_reply(struct fdisk_context *cxt, const char *prompt, + char *buf, size_t bufsz) +{ + char *p; + size_t sz; + + do { + fputs(prompt, stdout); + fflush(stdout); + + if (!fgets(buf, bufsz, stdin)) { + if (fdisk_label_is_changed(cxt->label)) { + fprintf(stderr, _("\nDo you really want to quit? ")); + + if (fgets(buf, bufsz, stdin) && !rpmatch(buf)) + continue; + } + fdisk_free_context(cxt); + exit(EXIT_FAILURE); + } else + break; + } while (1); + + for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */ + + if (p > buf) + memmove(buf, p, p - buf); /* remove blank space */ + sz = strlen(buf); + if (sz && *(buf + sz - 1) == '\n') + *(buf + sz - 1) = '\0'; + + DBG(ASK, dbgprint("user's reply: >>>%s<<<", buf)); + return 0; +} + +static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask, + char *buf, size_t bufsz) + +{ + const char *q = fdisk_ask_get_query(ask); + int dft = fdisk_ask_menu_get_default(ask); + + if (q) { + fputs(q, stdout); /* print header */ + fputc('\n', stdout); + } + + do { + char prompt[128]; + int key, c; + const char *name, *desc; + size_t i = 0; + + /* print menu items */ + while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) + fprintf(stdout, " %c %s (%s)\n", key, name, desc); + + /* ask for key */ + snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft); + get_user_reply(cxt, prompt, buf, bufsz); + if (!*buf) { + fdisk_info(cxt, _("Using default response %c."), dft); + c = dft; + } else + c = tolower(buf[0]); + + /* check result */ + i = 0; + while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) { + if (c == key) { + fdisk_ask_menu_set_result(ask, c); + return 0; /* success */ + } + } + fdisk_warnx(cxt, _("Value out of range.")); + } while (1); + + return -EINVAL; +} + + +#define tochar(num) ((int) ('a' + num - 1)) +static int ask_number(struct fdisk_context *cxt, + struct fdisk_ask *ask, + char *buf, size_t bufsz) +{ + char prompt[128] = { '\0' }; + const char *q = fdisk_ask_get_query(ask); + const char *range = fdisk_ask_number_get_range(ask); + + uint64_t dflt = fdisk_ask_number_get_default(ask), + low = fdisk_ask_number_get_low(ask), + high = fdisk_ask_number_get_high(ask); + int inchar = fdisk_ask_number_inchars(ask); + + assert(q); + + DBG(ASK, dbgprint("asking for number " + "['%s', <%ju,%ju>, default=%ju, range: %s]", + q, low, high, dflt, range)); + + if (range && dflt >= low && dflt <= high) { + if (inchar) + snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "), + q, range, tochar(dflt)); + else + snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), + q, range, dflt); + + } else if (dflt >= low && dflt <= high) { + if (inchar) + snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "), + q, tochar(low), tochar(high), tochar(dflt)); + else + snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), + q, low, high, dflt); + } else if (inchar) + snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "), + q, tochar(low), tochar(high)); + else + snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), + q, low, high); + + do { + int rc = get_user_reply(cxt, prompt, buf, bufsz); + uint64_t num; + + if (rc) + return rc; + if (!*buf && dflt >= low && dflt <= high) + return fdisk_ask_number_set_result(ask, dflt); + + if (isdigit_string(buf)) { + char *end; + + errno = 0; + num = strtoumax(buf, &end, 10); + if (errno || buf == end || (end && *end)) + continue; + } else if (inchar && isalpha(*buf)) { + num = tolower(*buf) - 'a' + 1; + } else + rc = -EINVAL; + + if (rc == 0 && num >= low && num <= high) + return fdisk_ask_number_set_result(ask, num); + + fdisk_warnx(cxt, _("Value out of range.")); + } while (1); + + return -1; +} + +static int ask_offset(struct fdisk_context *cxt, + struct fdisk_ask *ask, + char *buf, size_t bufsz) +{ + char prompt[128] = { '\0' }; + const char *q = fdisk_ask_get_query(ask); + const char *range = fdisk_ask_number_get_range(ask); + + uint64_t dflt = fdisk_ask_number_get_default(ask), + low = fdisk_ask_number_get_low(ask), + high = fdisk_ask_number_get_high(ask), + base = fdisk_ask_number_get_base(ask); + + assert(q); + + DBG(ASK, dbgprint("asking for offset ['%s', <%ju,%ju>, base=%ju, default=%ju, range: %s]", + q, low, high, base, dflt, range)); + + if (range && dflt >= low && dflt <= high) + snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), q, range, dflt); + else if (dflt >= low && dflt <= high) + snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), q, low, high, dflt); + else + snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), q, low, high); + + do { + uint64_t num = 0; + char sig = 0, *p; + int pwr = 0; + + int rc = get_user_reply(cxt, prompt, buf, bufsz); + if (rc) + return rc; + if (!*buf && dflt >= low && dflt <= high) + return fdisk_ask_number_set_result(ask, dflt); + + p = buf; + if (*p == '+' || *p == '-') { + sig = *buf; + p++; + } + + rc = parse_size(p, &num, &pwr); + if (rc) + continue; + DBG(ASK, dbgprint("parsed size: %ju", num)); + if (sig && pwr) { + /* +{size}{K,M,...} specified, the "num" is in bytes */ + uint64_t unit = fdisk_ask_number_get_unit(ask); + num += unit/2; /* round */ + num /= unit; + } + if (sig == '+') + num += base; + else if (sig == '-') + num = base - num; + + DBG(ASK, dbgprint("final offset: %ju [sig: %c, power: %d, %s]", + num, sig, pwr, + sig ? "relative" : "absolute")); + if (num >= low && num <= high) { + if (sig) + fdisk_ask_number_set_relative(ask, 1); + return fdisk_ask_number_set_result(ask, num); + } + fdisk_warnx(cxt, _("Value out of range.")); + } while (1); + + return -1; +} + +static unsigned int info_count; + +static void fputs_info(struct fdisk_ask *ask, FILE *out, char *buf, size_t bufsz) +{ + const char *msg; + unsigned int flags; + + assert(ask); + + msg = fdisk_ask_print_get_mesg(ask); + flags = fdisk_ask_get_flags(ask); + + if (!msg) + return; + if (info_count == 1) + fputc('\n', out); + if (flags == 0 || !colors_wanted()) + goto simple; + + if (flags & FDISK_INFO_COLON) { + size_t sz; + char *sep = _(": "); + char *p = strstr(msg, sep); + + if (!p) + goto simple; + + sz = strlen(sep); + strncpy(buf, msg, bufsz); + buf[p - msg + sz] = '\0'; + + color_enable(UL_COLOR_BROWN); + fputs(buf, out); + color_disable(); + fputs(p + sz, out); + + } else if (flags & FDISK_INFO_SUCCESS) { + color_enable(UL_COLOR_BOLD); + fputs(msg, out); + color_disable(); + } else { +simple: + fputs(msg, out); + } + fputc('\n', out); +} + +int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, + void *data __attribute__((__unused__))) +{ + int rc = 0; + char buf[BUFSIZ]; + + assert(cxt); + assert(ask); + + if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO) + info_count = 0; + + switch(fdisk_ask_get_type(ask)) { + case FDISK_ASKTYPE_MENU: + return ask_menu(cxt, ask, buf, sizeof(buf)); + case FDISK_ASKTYPE_NUMBER: + return ask_number(cxt, ask, buf, sizeof(buf)); + case FDISK_ASKTYPE_OFFSET: + return ask_offset(cxt, ask, buf, sizeof(buf)); + case FDISK_ASKTYPE_INFO: + info_count++; + fputs_info(ask, stdout, buf, sizeof(buf)); + break; + case FDISK_ASKTYPE_WARNX: + color_fenable(UL_COLOR_RED, stderr); + fputs(fdisk_ask_print_get_mesg(ask), stderr); + color_fdisable(stderr); + fputc('\n', stderr); + break; + case FDISK_ASKTYPE_WARN: + color_fenable(UL_COLOR_RED, stderr); + fputs(fdisk_ask_print_get_mesg(ask), stderr); + errno = fdisk_ask_print_get_errno(ask); + fprintf(stderr, ": %m\n"); + color_fdisable(stderr); + break; + case FDISK_ASKTYPE_YESNO: + fputc('\n', stdout); + fputs(fdisk_ask_get_query(ask), stdout); + rc = get_user_reply(cxt, _(" [Y]es/[N]o: "), buf, sizeof(buf)); + if (rc == 0) + fdisk_ask_yesno_set_result(ask, rpmatch(buf)); + DBG(ASK, dbgprint("yes-no ask: reply '%s' [rc=%d]", buf, rc)); + break; + case FDISK_ASKTYPE_STRING: + { + char prmt[BUFSIZ]; + snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask)); + fputc('\n', stdout); + rc = get_user_reply(cxt, prmt, buf, sizeof(buf)); + if (rc == 0) + fdisk_ask_string_set_result(ask, xstrdup(buf)); + DBG(ASK, dbgprint("string ask: reply '%s' [rc=%d]", buf, rc)); + break; + } + default: + warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask)); + return -EINVAL; + } + return rc; +} + +struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt) +{ + const char *q; + + if (!cxt || !cxt->label || !cxt->label->nparttypes) + return NULL; + + q = fdisk_is_parttype_string(cxt) ? + _("Partition type (type L to list all types): ") : + _("Hex code (type L to list all codes): "); + do { + char buf[256]; + int rc = get_user_reply(cxt, q, buf, sizeof(buf)); + + if (rc) + break; + + if (buf[1] == '\0' && toupper(*buf) == 'L') + list_partition_types(cxt); + else if (*buf) + return fdisk_parse_parttype(cxt, buf); + } while (1); + + return NULL; +} + +void list_partition_types(struct fdisk_context *cxt) +{ + struct fdisk_parttype *types; + size_t ntypes = 0; + + if (!cxt || !cxt->label || !cxt->label->parttypes) + return; + + types = cxt->label->parttypes; + ntypes = cxt->label->nparttypes; + + if (types[0].typestr == NULL) { + /* + * Prints in 4 columns in format + */ + size_t last[4], done = 0, next = 0, size; + int i; + + size = ntypes; + if (types[ntypes - 1].name == NULL) + size--; + + for (i = 3; i >= 0; i--) + last[3 - i] = done += (size + i - done) / (i + 1); + i = done = 0; + + do { + #define NAME_WIDTH 15 + char name[NAME_WIDTH * MB_LEN_MAX]; + size_t width = NAME_WIDTH; + struct fdisk_parttype *t = &types[next]; + size_t ret; + + if (t->name) { + printf("%c%2x ", i ? ' ' : '\n', t->type); + ret = mbsalign(_(t->name), name, sizeof(name), + &width, MBS_ALIGN_LEFT, 0); + + if (ret == (size_t)-1 || ret >= sizeof(name)) + printf("%-15.15s", _(t->name)); + else + fputs(name, stdout); + } + + next = last[i++] + done; + if (i > 3 || next >= last[i]) { + i = 0; + next = ++done; + } + } while (done < last[0]); + + } else { + /* + * Prints 1 column in format + */ + struct fdisk_parttype *t; + size_t i; + + for (i = 0, t = types; t && i < ntypes; t++, i++) { + if (t->name) + printf("%3zu %-30s %s\n", i + 1, + t->name, t->typestr); + } + } + putchar('\n'); +} + +void toggle_dos_compatibility_flag(struct fdisk_context *cxt) +{ + struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos"); + int flag; + + if (!lb) + return; + + flag = !fdisk_dos_is_compatible(lb); + fdisk_info(cxt, flag ? + _("DOS Compatibility flag is set (DEPRECATED!)") : + _("DOS Compatibility flag is not set")); + + fdisk_dos_enable_compatible(lb, flag); + + if (fdisk_is_disklabel(cxt, DOS)) + fdisk_reset_alignment(cxt); /* reset the current label */ +} + +void change_partition_type(struct fdisk_context *cxt) +{ + size_t i; + struct fdisk_parttype *t = NULL; + struct fdisk_partition *pa = NULL; + const char *old = NULL; + + assert(cxt); + assert(cxt->label); + + if (fdisk_ask_partnum(cxt, &i, FALSE)) + return; + + if (fdisk_get_partition(cxt, i, &pa)) { + fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1); + return; + } + + t = (struct fdisk_parttype *) fdisk_partition_get_type(pa); + old = t ? t->name : _("Unknown"); + + do { + t = ask_partition_type(cxt); + } while (!t); + + if (fdisk_set_partition_type(cxt, i, t) == 0) + fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, + _("Changed type of partition '%s' to '%s'."), + old, t ? t->name : _("Unknown")); + else + fdisk_info(cxt, + _("Type of partition %zu is unchanged: %s."), + i + 1, old); + + fdisk_unref_partition(pa); +} + +void list_disk_geometry(struct fdisk_context *cxt) +{ + char *id = NULL; + uint64_t bytes = cxt->total_sectors * cxt->sector_size; + char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE + | SIZE_SUFFIX_3LETTER, bytes); + + fdisk_colon(cxt, _("Disk %s: %s, %ju bytes, %ju sectors"), + cxt->dev_path, strsz, + bytes, (uintmax_t) cxt->total_sectors); + free(strsz); + + if (fdisk_require_geometry(cxt) || fdisk_context_use_cylinders(cxt)) + fdisk_colon(cxt, _("Geometry: %d heads, %llu sectors/track, %llu cylinders"), + cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders); + + fdisk_colon(cxt, _("Units: %s of %d * %ld = %ld bytes"), + fdisk_context_get_unit(cxt, PLURAL), + fdisk_context_get_units_per_sector(cxt), + cxt->sector_size, + fdisk_context_get_units_per_sector(cxt) * cxt->sector_size); + + fdisk_colon(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"), + cxt->sector_size, cxt->phy_sector_size); + fdisk_colon(cxt, _("I/O size (minimum/optimal): %lu bytes / %lu bytes"), + cxt->min_io_size, cxt->io_size); + if (cxt->alignment_offset) + fdisk_colon(cxt, _("Alignment offset: %lu bytes"), + cxt->alignment_offset); + if (fdisk_dev_has_disklabel(cxt)) + fdisk_colon(cxt, _("Disklabel type: %s"), cxt->label->name); + + if (fdisk_get_disklabel_id(cxt, &id) == 0 && id) + fdisk_colon(cxt, _("Disk identifier: %s"), id); +} + +void list_disklabel(struct fdisk_context *cxt) +{ + struct fdisk_table *tb = NULL; + char *str; + + /* print label specific stuff by libfdisk FDISK_ASK_INFO API */ + fdisk_list_disklabel(cxt); + + /* print partitions */ + if (fdisk_get_partitions(cxt, &tb)) + return; + if (fdisk_table_to_string(tb, cxt, NULL, 0, &str) == 0) { + fputc('\n', stdout); + if (str && *str) + fputs(str, stdout); + } + fdisk_unref_table(tb); +} + +static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz) +{ + size_t next; + const unsigned char *p0 = buf + i; + + for (next = i + 16; next < sz; next += 16) { + if (memcmp(p0, buf + next, 16) != 0) + break; + } + + return next == i + 16 ? i : next; +} + +static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all) +{ + size_t i, l, next = 0; + + if (!buf) + return; + for (i = 0, l = 0; i < sz; i++, l++) { + if (l == 0) { + if (all == 0 && !next) + next = skip_empty(buf, i, sz); + printf("%08jx ", base + i); + } + printf(" %02x", buf[i]); + if (l == 7) /* words separator */ + fputs(" ", stdout); + else if (l == 15) { + fputc('\n', stdout); /* next line */ + l = -1; + if (next > i) { + printf("*\n"); + i = next - 1; + } + next = 0; + } + } + if (l > 0) + printf("\n"); +} + +static void dump_blkdev(struct fdisk_context *cxt, const char *name, + off_t offset, size_t size, int all) +{ + fdisk_colon(cxt, _("\n%s: offset = %ju, size = %zu bytes."), + name, offset, size); + + if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1) + fdisk_warn(cxt, _("cannot seek")); + else { + unsigned char *buf = xmalloc(size); + + if (read_all(cxt->dev_fd, (char *) buf, size) != (ssize_t) size) + fdisk_warn(cxt, _("cannot read")); + else + dump_buffer(offset, buf, size, all); + free(buf); + } +} + +void dump_firstsector(struct fdisk_context *cxt) +{ + int all = !isatty(STDOUT_FILENO); + + assert(cxt); + assert(cxt->label); + + dump_blkdev(cxt, _("First sector"), 0, cxt->sector_size, all); +} + +void dump_disklabel(struct fdisk_context *cxt) +{ + int all = !isatty(STDOUT_FILENO); + int i = 0; + const char *name = NULL; + off_t offset = 0; + size_t size = 0; + + assert(cxt); + assert(cxt->label); + + while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size) + dump_blkdev(cxt, name, offset, size, all); +} + +static int is_ide_cdrom_or_tape(char *device) +{ + int fd, ret; + + if ((fd = open(device, O_RDONLY)) < 0) + return 0; + ret = blkdev_is_cdrom(fd); + + close(fd); + return ret; +} + +static void print_device_pt(struct fdisk_context *cxt, char *device) +{ + if (fdisk_context_assign_device(cxt, device, 1) != 0) /* read-only */ + err(EXIT_FAILURE, _("cannot open %s"), device); + + list_disk_geometry(cxt); + + if (fdisk_dev_has_disklabel(cxt)) + list_disklabel(cxt); + fputc('\n', stdout); +} + +static void print_all_devices_pt(struct fdisk_context *cxt) +{ + FILE *f; + char line[128 + 1]; + + f = fopen(_PATH_PROC_PARTITIONS, "r"); + if (!f) { + warn(_("cannot open %s"), _PATH_PROC_PARTITIONS); + return; + } + + DBG(FRONTEND, dbgprint("reading "_PATH_PROC_PARTITIONS)); + + while (fgets(line, sizeof(line), f)) { + char ptname[128 + 1], devname[256]; + + if (sscanf(line, " %*d %*d %*d %128[^\n ]", ptname) != 1) + continue; + + snprintf(devname, sizeof(devname), "/dev/%s", ptname); + + DBG(FRONTEND, dbgprint("listing %s", devname)); + + if (is_whole_disk(devname)) { + char *cn = canonicalize_path(devname); + if (cn) { + if (!is_ide_cdrom_or_tape(cn)) + print_device_pt(cxt, cn); + free(cn); + } + } + } + fclose(f); +} + +static sector_t get_dev_blocks(char *dev) +{ + int fd; + sector_t size; + + if ((fd = open(dev, O_RDONLY)) < 0) + err(EXIT_FAILURE, _("cannot open %s"), dev); + if (blkdev_get_sectors(fd, &size) == -1) { + close(fd); + err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev); + } + close(fd); + return size/2; +} + +enum { + ACT_FDISK = 0, /* default */ + ACT_LIST, + ACT_SHOWSIZE +}; + +int main(int argc, char **argv) +{ + int i, c, act = ACT_FDISK; + int colormode = UL_COLORMODE_UNDEF; + struct fdisk_context *cxt; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + fdisk_init_debug(0); + cxt = fdisk_new_context(); + if (!cxt) + err(EXIT_FAILURE, _("failed to allocate libfdisk context")); + + fdisk_context_set_ask(cxt, ask_callback, NULL); + + while ((c = getopt(argc, argv, "b:c::C:hH:lL::sS:t:u::vV")) != -1) { + switch (c) { + case 'b': + { + size_t sz = strtou32_or_err(optarg, + _("invalid sector size argument")); + if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096) + usage(stderr); + fdisk_save_user_sector_size(cxt, sz, sz); + break; + } + case 'C': + fdisk_save_user_geometry(cxt, + strtou32_or_err(optarg, + _("invalid cylinders argument")), + 0, 0); + break; + case 'c': + if (optarg) { + /* this setting is independent on the current + * actively used label */ + struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos"); + if (!lb) + err(EXIT_FAILURE, _("not found DOS label driver")); + if (strcmp(optarg, "=dos") == 0) + fdisk_dos_enable_compatible(lb, TRUE); + else if (strcmp(optarg, "=nondos") == 0) + fdisk_dos_enable_compatible(lb, FALSE); + else + usage(stderr); + } + /* use default if no optarg specified */ + break; + case 'H': + fdisk_save_user_geometry(cxt, 0, + strtou32_or_err(optarg, + _("invalid heads argument")), + 0); + break; + case 'S': + fdisk_save_user_geometry(cxt, 0, 0, + strtou32_or_err(optarg, + _("invalid sectors argument"))); + break; + case 'l': + act = ACT_LIST; + break; + case 'L': + if (optarg) + colormode = colormode_or_err(optarg, + _("unsupported color mode")); + break; + case 's': + act = ACT_SHOWSIZE; + break; + case 't': + { + struct fdisk_label *lb = NULL; + + while (fdisk_context_next_label(cxt, &lb) == 0) + fdisk_label_set_disabled(lb, 1); + + lb = fdisk_context_get_label(cxt, optarg); + if (!lb) + errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg); + fdisk_label_set_disabled(lb, 0); + } + case 'u': + if (optarg && *optarg == '=') + optarg++; + if (fdisk_context_set_unit(cxt, optarg) != 0) + usage(stderr); + break; + case 'V': + case 'v': + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case 'h': + usage(stdout); + default: + usage(stderr); + } + } + + if (argc-optind != 1 && fdisk_has_user_device_properties(cxt)) + warnx(_("The device properties (sector size and geometry) should" + " be used with one specified device only.")); + + colors_init(colormode, "fdisk"); + + switch (act) { + case ACT_LIST: + fdisk_context_enable_listonly(cxt, 1); + + if (argc > optind) { + int k; + for (k = optind; k < argc; k++) + print_device_pt(cxt, argv[k]); + } else + print_all_devices_pt(cxt); + break; + + case ACT_SHOWSIZE: + /* deprecated */ + if (argc - optind <= 0) + usage(stderr); + + for (i = optind; i < argc; i++) { + if (argc - optind == 1) + printf("%llu\n", get_dev_blocks(argv[i])); + else + printf("%s: %llu\n", argv[i], get_dev_blocks(argv[i])); + } + break; + + case ACT_FDISK: + if (argc-optind != 1) + usage(stderr); + + /* Here starts interactive mode, use fdisk_{warn,info,..} functions */ + color_enable(UL_COLOR_GREEN); + fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING); + color_disable(); + fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n" + "Be careful before using the write command.\n")); + + if (fdisk_context_assign_device(cxt, argv[optind], 0) != 0) + err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); + + fflush(stdout); + + if (!fdisk_dev_has_disklabel(cxt)) { + fdisk_info(cxt, _("Device does not contain a recognized partition table.")); + fdisk_create_disklabel(cxt, NULL); + + } else if (fdisk_is_disklabel(cxt, GPT) && fdisk_gpt_is_hybrid(cxt)) + fdisk_warnx(cxt, _( + "The hybrid GPT detected. You have to sync " + "the hybrid MBR manually (expert command 'M').")); + + while (1) + process_fdisk_menu(&cxt); + } + + fdisk_free_context(cxt); + return EXIT_SUCCESS; +} diff --git a/disk-utils/fdisk.h b/disk-utils/fdisk.h new file mode 100644 index 000000000..8d8144a7c --- /dev/null +++ b/disk-utils/fdisk.h @@ -0,0 +1,36 @@ +#ifndef UTIL_LINUX_FDISK_H +#define UTIL_LINUX_FDISK_H +/* + fdisk.h +*/ + +#include "c.h" + +/* Let's temporary include private libfdisk header file. The final libfdisk.h + * maybe included when fdisk.c and libfdisk code will be completely spit. + */ +#include "fdiskP.h" +#include "blkdev.h" +#include "colors.h" + +extern int get_user_reply(struct fdisk_context *cxt, + const char *prompt, + char *buf, size_t bufsz); +extern int process_fdisk_menu(struct fdisk_context **cxt); + +extern int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, + void *data __attribute__((__unused__))); + +/* prototypes for fdisk.c */ +extern void dump_firstsector(struct fdisk_context *cxt); +extern void dump_disklabel(struct fdisk_context *cxt); + +extern void list_partition_types(struct fdisk_context *cxt); +extern void list_disk_geometry(struct fdisk_context *cxt); +extern void list_disklabel(struct fdisk_context *cxt); +extern void change_partition_type(struct fdisk_context *cxt); +extern struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt); + +extern void toggle_dos_compatibility_flag(struct fdisk_context *cxt); + +#endif /* UTIL_LINUX_FDISK_H */ diff --git a/disk-utils/sfdisk.8 b/disk-utils/sfdisk.8 new file mode 100644 index 000000000..62d0ed095 --- /dev/null +++ b/disk-utils/sfdisk.8 @@ -0,0 +1,650 @@ +.\" Copyright 1995 Andries E. Brouwer (aeb@cwi.nl) +.\" May be distributed under the GNU General Public License +.\" The `DOS 6.x Warning' was taken from the old fdisk.8, which says +.\" -- Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) +.\" -- May be distributed under the GNU General Public License +.\" The `DRDOS Warning' was taken from a net post by Stephen Tweedie. +.\" +.TH SFDISK 8 "August 2011" "util-linux" "System Administration" +.SH NAME +sfdisk \- partition table manipulator for Linux +.SH SYNOPSIS +.B sfdisk +.RB [ options ] +.I device +.br +.B sfdisk \-s +.RI [ partition ] +.SH DESCRIPTION +.B sfdisk +has four (main) uses: list the size of a partition, list the partitions +on a device, check the partitions on a device, and \- very dangerous \- +repartition a device. + +.B sfdisk +doesn't understand the GUID Partition Table (GPT) format and it is not +designed for large partitions. In these cases use the more advanced GNU +.BR parted (8). + +Note that +.B sfdisk +does not align partitions to block-device I/O limits. This functionality is +provided by +.BR fdisk (8). + +.SS "List sizes" +.BI "sfdisk \-s " partition +gives the size of +.I partition +in blocks. This may be useful in connection with programs like +.BR mkswap (8). +Here +.I partition +is usually something like +.I /dev/hda1 +or +.IR /dev/sdb12 , +but may also be an entire disk, like +.IR /dev/xda . + +.RS +.nf +.if t .ft CW +% sfdisk \-s /dev/hda9 +81599 +.if t .ft R +.fi +.RE + +If the partition argument is omitted, +.B sfdisk +will list the sizes of all block devices, and the total: + +.RS +.nf +.if t .ft CW +% sfdisk \-s +/dev/hda: 208896 +/dev/hdb: 1025136 +/dev/hdc: 1031063 +/dev/sda: 8877895 +/dev/sdb: 1758927 +total: 12901917 blocks +.if t .ft R +.fi +.RE + +.SS "List partitions" +The second type of invocation: +.BI "sfdisk \-l " device +will list the partitions on the specified device. If the +.I device +argument is omitted, the partitions on all block devices are listed. + +.RS +.nf +.if t .ft CW +% sfdisk \-l /dev/hdc + +Disk /dev/hdc: 16 heads, 63 sectors, 2045 cylinders +Units = cylinders of 516096 bytes, blocks of 1024 bytes, counting from 0 + + Device Boot Start End #cyls #blocks Id System +/dev/hdc1 0+ 406 407\- 205096+ 83 Linux native +/dev/hdc2 407 813 407 205128 83 Linux native +/dev/hdc3 814 2044 1231 620424 83 Linux native +/dev/hdc4 0 \- 0 0 0 Empty +.if t .ft R +.fi +.RE + +The trailing \- and + signs indicate that rounding has taken place, +and that the actual value is slightly less or more. To see the +exact values, ask for a listing with sectors as unit (\fB\-u S\fR). + +.SS "Check partitions" +The third type of invocation: +.BI "sfdisk \-V " device +will apply various consistency checks to the partition tables on +.IR device . +It prints `OK' or complains. The \fB\-V\fR option can be used +together with \fB\-l\fR. In a shell script one might use +.BI "sfdisk \-V \-q " device +which only returns a status. + +.SS "Create partitions" +The fourth type of invocation: +.BI "sfdisk " device +will cause +.B sfdisk +to read the specification for the desired partitioning of +.I device +from standard input, and then to change the partition tables +on that block device. Thus it is possible to use +.B sfdisk +from a shell script. When +.B sfdisk +determines that its standard input is a terminal, it will be +conversational; otherwise it will abort on any error. +.LP +BE EXTREMELY CAREFUL - ONE TYPING MISTAKE AND ALL YOUR DATA IS LOST +.LP +As a precaution, one can save the sectors changed by +.BR sfdisk : + +.RS +.nf +.if t .ft CW +% sfdisk /dev/hdd \-O hdd-partition-sectors.save +\&... +.if t .ft R +.fi +.RE + +.LP +Then, if you discover that you did something stupid before anything +else has been written to the block device, it may be possible to recover +the old situation with: + +.RS +.nf +.if t .ft CW +% sfdisk /dev/hdd \-I hdd-partition-sectors.save +.if t .ft R +.fi +.RE + +.LP +(This is not the same as saving the old partition table: +a readable version of the old partition table can be saved +using the \fB\-d\fR option. However, if you create logical partitions, +the sectors describing them are located somewhere on block device, +possibly on sectors that were not part of the partition table +before. Thus, the information the \fB\-O\fR option saves +is not a binary version of the output of \fB\-d\fR.) + +There are many options. + +.SH OPTIONS +.TP +.BR \-v ", " \-\-version +Display version information and exit. +.TP +.BR \-h ", " \-\-help +Display help text and exit. +.TP +.BR \-T ", " \-\-list\-types +Print the recognized types (system Id's). +.TP +.BR \-s ", " \-\-show\-size +List the size of a partition. +.TP +.BR \-g ", " \-\-show\-geometry +List the kernel's idea of the geometry of the indicated block device(s). +.TP +.BR \-G ", " \-\-show\-pt\-geometry +List the geometry of the indicated block devices guessed by looking at +the partition table. +.TP +.BR \-l ", " \-\-list +List the partitions of a device. +.TP +.BR \-d ", " \-\-dump +Dump the partitions of a device in a format that is usable as input +to \fBsfdisk\fR. For example, +.br +.nf +.if t .ft CW + % sfdisk -d /dev/hda > hda.out + % sfdisk /dev/hda < hda.out +.if t .ft R +.fi +will correct the bad last extended partition that the OS/2 +fdisk creates. +.TP +.BR \-V ", " \-\-verify +Test whether partitions seem correct. (See the third invocation type above.) +.TP +.BR \-i ", " \-\-increment +Number cylinders etc. starting from 1 instead of 0. +.TP +.BI \-N " number" +Change only the single partition indicated. For example: +.nf +.if t .ft CW + % sfdisk /dev/hdb \-N5 + ,,,* +.if t .ft R +.fi +will make the fifth partition on /dev/hdb bootable (`active') +and change nothing else. (Probably this fifth partition +is called /dev/hdb5, but you are free to call it something else, +like `/my_equipment/disks/2/5' or so). +.TP +\fB\-A\fR, \fB\-\-activate\fR[=\fIdevice_or_number\fR] +Switch on the bootable flag. +.IP +This option takes an optional argument. When no option argument is given, +the command will list the partitions that have the bootable flag set +for the device specified as command argument. For example: +.IP +.nf +.if t .ft CW + % sfdisk --activate /dev/sda +.fi +.IP +When a device name is given as option argument, the partitions specified +as command argument will have the bootable flag switched on. +Other partitions for the same device will have the bootable flag cleared. +For example, with the following command the partitions 1 and 4 are set +to be bootable, while 2 and 3 are cleared: +.IP +.nf +.if t .ft CW + % sfdisk --activate=/dev/sda 1 4 +.fi +.IP +If only a single partition needs to be activated, then the partition number +must be given as option argument, and the device as command argument. For example: +.IP +.nf +.if t .ft CW + % sfdisk --activate=1 /dev/sda +.fi +.IP +The activate option is turned by default on when the program's invocation name is +.BR activate . +.TP +.BR \-c ", " \-\-id " \fInumber\fR [\fIId\fR]" +If no \fIId\fR argument given: print the partition Id of the indicated +partition. If an \fIId\fR argument is present: change the type (Id) of +the indicated partition to the given value. +This option has two longer forms, \fB\-\-print\-id\fR and \fB\-\-change\-id\fR. +For example: +.br +.nf +.if t .ft CW + % sfdisk --print-id /dev/hdb 5 + 6 + % sfdisk --change-id /dev/hdb 5 83 + OK +.if t .ft R +.fi +first reports that /dev/hdb5 has Id 6, and then changes that into 83. +.TP +.BR \-u ", " \-\-unit " \fIletter\fR" +Interpret the input and show the output in the units specified by +.IR letter . +This \fIletter\fR can be one of S, C, B or M, meaning Sectors, Cylinders, +Blocks and Megabytes, respectively. The default is +cylinders, at least when the geometry is known. +.TP +.BR \-x ", " \-\-show\-extended +Also list non-primary extended partitions on output, +and expect descriptors for them on input. +.TP +.BR \-C ", " \-\-cylinders " \fIcylinders\fR" +Specify the number of cylinders, possibly overriding what the kernel thinks. +.TP +.BR \-H ", " \-\-heads " \fIheads\fR" +Specify the number of heads, possibly overriding what the kernel thinks. +.TP +.BR \-S ", " \-\-sectors " \fIsectors\fR" +Specify the number of sectors, possibly overriding what the kernel thinks. +.TP +.BR \-f ", " \-\-force +Do what I say, even if it is stupid. +.TP +.BR \-q ", " \-\-quiet +Suppress warning messages. +.TP +.BR \-L ", " \-\-Linux +Do not complain about things irrelevant for Linux. +.TP +.BR \-D ", " \-\-DOS +For DOS-compatibility: waste a little space. +(More precisely: if a partition cannot contain sector 0, +e.g. because that is the MBR of the device, or contains +the partition table of an extended partition, then +.B sfdisk +would make it start the next sector. However, when this +option is given it skips to the start of the next track, +wasting for example 33 sectors (in case of 34 sectors/track), +just like certain versions of DOS do.) +Certain Disk Managers and boot loaders (such as OSBS, but not +LILO or the OS/2 Boot Manager) also live in this empty space, +so maybe you want this option if you use one. +.TP +.BR \-E ", " \-\-DOS\-extended +Take the starting sector numbers of "inner" extended partitions +to be relative to the starting cylinder boundary of the outer one +(like some versions of DOS do), rather than relative to the actual +starting sector (like Linux does). +(The fact that there is a difference here means that one should +always let extended partitions start at cylinder boundaries if +DOS and Linux should interpret the partition table in the same way. +Of course one can only know where cylinder boundaries are when +one knows what geometry DOS will use for this block device.) +.TP +.BR \-U ", " "\-\-unhide " \fIdevice\fR +Make various Microsoft partition types unhidden. For full list see types +output. +.IP +.nf +.if t .ft CW + % sfdisk --list-types | grep Hidden +.fi +.IP +Notice that the +.B Hidden NTFS WinRE +(Windows Recovery Environment) does not have non-hidden equivalent. +.TP +.BR \-\-IBM ", " \-\-leave\-last +Certain IBM diagnostic programs assume that they can use the +last cylinder on a device for disk-testing purposes. If you think +you might ever run such programs, use this option to tell +.B sfdisk +that it should not allocate the last cylinder. +Sometimes the last cylinder contains a bad sector table. +.TP +.B \-n +Go through all the motions, but do not actually write to block device. +.TP +.BR \-R ", " \-\-re-read +Only execute the BLKRRPART ioctl (to make the kernel re-read +the partition table). This can be useful for checking in advance +that the final BLKRRPART will be successful, and also when you +changed the partition table `by hand' (e.g., using dd from a backup). +If the kernel complains (`device busy for revalidation (usage = 2)') +then something still uses the device, and you still have to unmount +some file system, or say swapoff to some swap partition. +.TP +.B \-\-no\-reread +When starting a repartitioning of a block device, \fBsfdisk\fR checks that this device +is not mounted, or in use as a swap device, and refuses to continue +if it is. This option suppresses the test. (On the other hand, the \fB\-f\fR +option would force \fBsfdisk\fR to continue even when this test fails.) +.TP +.B \-\-in\-order +Partitions are in order. See also warning section. +.TP +.B \-\-not\-in\-order +Partitions are not in order. See also warning section. +.TP +.B \-\-inside\-outer +All logical partitions are inside outermost extended. See also warning +section and chaining. +.TP +.B \-\-not\-inside\-outer +Some, or none, of the logical partitions are not inside outermost +extended. See also warning section and chaining. +.TP +.B \-\-nested +Caution, see warning section. Every partition is contained in the +surrounding partitions and is disjoint from all others. +.TP +.B \-\-chained +Caution, see warning section. Every data partition is contained in +the surrounding partitions and disjoint from all others, but +extended partitions may lie outside (insofar as allowed by +all_logicals_inside_outermost_extended). +.TP +.B \-\-onesector +Caution, see warning section. All data partitions are mutually +disjoint; extended partitions each use one sector only (except +perhaps for the outermost one). +.TP +.BI \-O " file" +Just before writing the new partition, output the sectors +that are going to be overwritten to +.I file +(where hopefully +.I file +resides on another block device, or on a floppy). +.TP +.BI \-I " file" +After destroying your filesystems with an unfortunate +.B sfdisk +command, you would have been able to restore the old situation +if only you had preserved it using the \fB\-O\fR flag. +.TP +.BR \-1 ", " \-\-one-only +Reserved option that does nothing currently. + +.SH THEORY +Block 0 of a block device (the Master Boot Record) contains among +other things four partition descriptors. The partitions +described here are called +.I primary +partitions. +.LP +A partition descriptor has 6 fields: +.br +.nf +.RS +struct partition { + unsigned char bootable; /* 0 or 0x80 */ + hsc begin_hsc; + unsigned char id; + hsc end_hsc; + unsigned int starting_sector; + unsigned int nr_of_sectors; +} +.RE +.fi +.LP +The two hsc fields indicate head, sector and cylinder of the +begin and the end of the partition. Since each hsc field only +takes 3 bytes, only 24 bits are available, which does not +suffice for big block devices (say > 8GB). In fact, due to the wasteful +representation (that uses a byte for the number of heads, which +is typically 16), problems already start with 0.5GB. +However Linux does not use these fields, and problems can arise +only at boot time, before Linux has been started. For more +details, see the +.B lilo +documentation. +.LP +Each partition has a type, its `Id', and if this type is 5 or f +.IR "" "(`" "extended partition" "')" +the starting sector of the partition +again contains 4 partition descriptors. MSDOS only uses the +first two of these: the first one an actual data partition, +and the second one again an extended partition (or empty). +In this way one gets a chain of extended partitions. +Other operating systems have slightly different conventions. +Linux also accepts type 85 as equivalent to 5 and f - this can be +useful if one wants to have extended partitions under Linux past +the 1024 cylinder boundary, without DOS FDISK hanging. +(If there is no good reason, you should just use 5, which is +understood by other systems.) +.LP +Partitions that are not primary or extended are called +.IR logical . +Often, one cannot boot from logical partitions (because the +process of finding them is more involved than just looking +at the MBR). +Note that of an extended partition only the Id and the start +are used. There are various conventions about what to write +in the other fields. One should not try to use extended partitions +for data storage or swap. + +.SH "INPUT FORMAT" +.B sfdisk +reads lines of the form +.br +.RS + +.RE +where each line fills one partition descriptor. +.LP +Fields are separated by whitespace, or comma or semicolon possibly +followed by whitespace; initial and trailing whitespace is ignored. +Numbers can be octal, decimal or hexadecimal, decimal is default. +When a field is absent or empty, a default value is used. +.LP +The parts can (and probably should) be omitted - +.B sfdisk +computes them from and and the block device geometry +as given by the kernel or specified using the \-H, \-S, \-C flags. +.LP +Bootable is specified as [*|\-], with as default not-bootable. +(The value of this field is irrelevant for Linux - when Linux +runs it has been booted already - but might play a role for +certain boot loaders and for other operating systems. +For example, when there are several primary DOS partitions, +DOS assigns C: to the first among these that is bootable.) +.LP +Id is given in hex, without the 0x prefix, or is [E|S|L|X], where +L (LINUX_NATIVE (83)) is the default, S is LINUX_SWAP (82), E +is EXTENDED_PARTITION (5), and X is LINUX_EXTENDED (85). +.LP +The default value of start is the first nonassigned sector/cylinder/... +.LP +The default value of size is as much as possible (until next +partition or end-of-device). +.LP +However, for the four partitions inside an extended partition, +the defaults are: Linux partition, Extended partition, Empty, Empty. +.LP +But when the \-N option (change a single partition only) is given, +the default for each field is its previous value. +.LP +A '+' can be specified instead of a number for size, which means +as much as possible. This is useful with the \-N option. +.SH EXAMPLE +The command +.RS +.nf +.if t .ft CW +sfdisk /dev/hdc << EOF +0,407 +,407 +; +; +EOF +.if t .ft R +.fi +.RE +will partition /dev/hdc just as indicated above. + +The command +.RS +.nf +.if t .ft CW +sfdisk /dev/hdb << EOF +,3,L +,60,L +,19,S +,,E +,130,L +,130,L +,130,L +,,L +EOF +.if t .ft R +.fi +.RE +will partition /dev/hdb into two Linux partitions of 3 and 60 +cylinders, a swap space of 19 cylinders, and an extended partition +covering the rest. Inside the extended partition there are four +Linux logical partitions, three of 130 cylinders and one +covering the rest. + +With the \-x option, the number of input lines must be a multiple of 4: +you have to list the two empty partitions that you never want +using two blank lines. Without the \-x option, you give one line +for the partitions inside a extended partition, instead of four, +and terminate with end-of-file (^D). +(And +.B sfdisk +will assume that your input line represents the first of four, +that the second one is extended, and the 3rd and 4th are empty.) +.SH "CAUTION WARNINGS" + +The options marked with caution in the manual page are dangerous. +For example not all functionality is completely implemented, +which can be a reason for unexpected results. +.SH "DOS 6.x WARNING" + +The DOS 6.x FORMAT command looks for some information in the first +sector of the data area of the partition, and treats this information +as more reliable than the information in the partition table. DOS +FORMAT expects DOS FDISK to clear the first 512 bytes of the data area +of a partition whenever a size change occurs. DOS FORMAT will look at +this extra information even if the /U flag is given -- we consider +this a bug in DOS FORMAT and DOS FDISK. +.LP +The bottom line is that if you use sfdisk to change the size of a +DOS partition table entry, then you must also use +.B dd +to zero the first 512 bytes of that partition before using DOS FORMAT to +format the partition. For example, if you were using sfdisk to make a DOS +partition table entry for /dev/hda1, then (after exiting sfdisk and +rebooting Linux so that the partition table information is valid) you +would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero +the first 512 bytes of the partition. +.B BE EXTREMELY CAREFUL +if you use the +.B dd +command, since a small typo can make all of the data on your block device useless. + +For best results, you should always use an OS-specific partition table +program. For example, you should make DOS partitions with the DOS FDISK +program and Linux partitions with the Linux sfdisk program. + +.SH "DRDOS WARNINGS" + +Stephen Tweedie reported (930515): `Most reports of superblock +corruption turn out to be due to bad partitioning, with one filesystem +overrunning the start of the next and corrupting its superblock. +I have even had this problem with the supposedly-reliable DRDOS. This +was quite possibly due to DRDOS-6.0's FDISK command. Unless I created +a blank track or cylinder between the DRDOS partition and the +immediately following one, DRDOS would happily stamp all over the +start of the next partition. Mind you, as long as I keep a little +free device space after any DRDOS partition, I don't have any other +problems with the two coexisting on the one drive.' + +A. V. Le Blanc writes in README.efdisk: `Dr. DOS 5.0 and 6.0 has been +reported to have problems cooperating with Linux, and with this version +of efdisk in particular. This efdisk sets the system type +to hexadecimal 81. Dr. DOS seems to confuse +this with hexadecimal 1, a DOS code. If you use Dr. DOS, use the +efdisk command 't' to change the system code of any Linux partitions +to some number less than hexadecimal 80; I suggest 41 and 42 for +the moment.' + +A. V. Le Blanc writes in his README.fdisk: `DR-DOS 5.0 and 6.0 +are reported to have difficulties with partition ID codes of 80 or more. +The Linux `fdisk' used to set the system type +of new partitions to hexadecimal 81. DR-DOS seems to confuse this with +hexadecimal 1, a DOS code. The values 82 for swap and 83 for file +systems should not cause problems with DR-DOS. If they do, you may use +the `fdisk' command `t' to change the system code of any Linux +partitions to some number less than hexadecimal 80; I suggest 42 and 43 +for the moment.' + +In fact, it seems that only 4 bits are significant for the DRDOS FDISK, +so that for example 11 and 21 are listed as DOS 2.0. However, DRDOS +itself seems to use the full byte. I have not been able to reproduce +any corruption with DRDOS or its fdisk. + +.SH BUGS +There are too many options. +.LP +There is no support for non-DOS partition types. + +.\" .SH AUTHOR +.\" A. E. Brouwer (aeb@cwi.nl) +.\" +.SH "SEE ALSO" +.BR cfdisk (8), +.BR fdisk (8), +.BR mkfs (8), +.BR parted (8), +.BR partprobe (8), +.BR kpartx (8) +.SH AVAILABILITY +The sfdisk command is part of the util-linux package and is available from +ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/disk-utils/sfdisk.c b/disk-utils/sfdisk.c new file mode 100644 index 000000000..bd32aa8a5 --- /dev/null +++ b/disk-utils/sfdisk.c @@ -0,0 +1,3230 @@ +/* + * sfdisk version 3.0 - aeb - 950813 + * + * Copyright (C) 1995 Andries E. Brouwer (aeb@cwi.nl) + * + * 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 1 + * or (at your option) any later version. + * + * A.V. Le Blanc (LeBlanc@mcc.ac.uk) wrote Linux fdisk 1992-1994, + * patched by various people (faith@cs.unc.edu, martin@cs.unc.edu, + * leisner@sdsp.mc.xerox.com, esr@snark.thyrsus.com, aeb@cwi.nl) + * 1993-1995, with version numbers (as far as I have seen) 0.93 - 2.0e. + * This program had (head,sector,cylinder) as basic unit, and was + * (therefore) broken in several ways for the use on larger disks - + * for example, my last patch (from 2.0d to 2.0e) was required + * to allow a partition to cross cylinder 8064, and to write an + * extended partition past the 4GB mark. + * + * The current program is a rewrite from scratch, and I started a + * version numbering at 3.0. + * Andries Brouwer, aeb@cwi.nl, 950813 + * + * Well, a good user interface is still lacking. On the other hand, + * many configurations cannot be handled by any other fdisk. + * I changed the name to sfdisk to prevent confusion. - aeb, 970501 + */ + +#include +#include /* atoi, free */ +#include /* varargs */ +#include /* read, write */ +#include /* O_RDWR */ +#include /* ERANGE */ +#include /* strchr(), strrchr() */ +#include +#include +#include +#include +#include +#include + +#include "c.h" +#include "nls.h" +#include "xalloc.h" +#include "blkdev.h" +#include "linux_version.h" +#include "wholedisk.h" +#include "pathnames.h" +#include "canonicalize.h" +#include "rpmatch.h" +#include "closestream.h" +#include "strutils.h" + +struct systypes { + unsigned char type; + char *name; +}; + +static struct systypes i386_sys_types[] = { + #include "pt-mbr-partnames.h" +}; + +static char *partname(char *dev, int pno, int lth); + +/* + * Table of contents: + * A. About seeking + * B. About sectors + * C. About heads, sectors and cylinders + * D. About system Ids + * E. About partitions + * F. The standard input + * G. The command line + * H. Listing the current situation + * I. Writing the new situation + */ +int exit_status = 0; + +int force = 0; /* 1: do what I say, even if it is stupid ... */ +int quiet = 0; /* 1: suppress all warnings */ +/* IA-64 gcc spec file currently does -DLinux... */ +#undef Linux +int Linux = 0; /* 1: suppress warnings irrelevant for Linux */ +int DOS = 0; /* 1: shift extended partitions by #sectors, not 1 */ +int DOS_extended = 0; /* 1: use starting cylinder boundary of extd partn */ +int dump = 0; /* 1: list in a format suitable for later input */ +int verify = 0; /* 1: check that listed partition is reasonable */ +int no_write = 0; /* 1: do not actually write to disk */ +int no_reread = 0; /* 1: skip the BLKRRPART ioctl test at startup */ +int leave_last = 0; /* 1: don't allocate the last cylinder */ +int opt_list = 0; +char *save_sector_file = NULL; +char *restore_sector_file = NULL; + +/* + * A. About seeking + */ + +/* + * sseek: seek to specified sector - return 0 on failure + * + * Note: we use 512-byte sectors here, irrespective of the hardware ss. + */ + +static int +sseek(char *dev, int fd, unsigned long s) { + off_t in, out; + in = ((off_t) s << 9); + + if ((out = lseek(fd, in, SEEK_SET)) != in) { + warn(_("seek error on %s - cannot seek to %lu"), dev, s); + return 0; + } + + if (in != out) { + warnx(_("seek error: wanted 0x%08x%08x, got 0x%08x%08x"), + (unsigned int)(in >> 32), (unsigned int)(in & 0xffffffff), + (unsigned int)(out >> 32), (unsigned int)(out & 0xffffffff)); + return 0; + } + return 1; +} + +/* + * B. About sectors + */ + +/* + * We preserve all sectors read in a chain - some of these will + * have to be modified and written back. + */ +struct sector { + struct sector *next; + unsigned long long sectornumber; + int to_be_written; + char data[512]; +} *sectorhead; + +static void +free_sectors(void) { + struct sector *s; + + while (sectorhead) { + s = sectorhead; + sectorhead = s->next; + free(s); + } +} + +static struct sector * +get_sector(char *dev, int fd, unsigned long long sno) { + struct sector *s; + + for (s = sectorhead; s; s = s->next) + if (s->sectornumber == sno) + return s; + + if (!sseek(dev, fd, sno)) + return 0; + + s = xmalloc(sizeof(struct sector)); + + if (read(fd, s->data, sizeof(s->data)) != sizeof(s->data)) { + warn(_("read error on %s - cannot read sector %llu"), dev, sno); + free(s); + return 0; + } + + s->next = sectorhead; + sectorhead = s; + s->sectornumber = sno; + s->to_be_written = 0; + + return s; +} + +static int +msdos_signature(struct sector *s) { + unsigned char *data = (unsigned char *)s->data; + if (data[510] == 0x55 && data[511] == 0xaa) + return 1; + return 0; +} + +static int +write_sectors(char *dev, int fd) { + struct sector *s; + + for (s = sectorhead; s; s = s->next) + if (s->to_be_written) { + if (!sseek(dev, fd, s->sectornumber)) + return 0; + if (write(fd, s->data, sizeof(s->data)) != sizeof(s->data)) { + warn(_("write error on %s - cannot write sector %llu"), + dev, s->sectornumber); + return 0; + } + s->to_be_written = 0; + } + return 1; +} + +static void +ulong_to_chars(unsigned long u, char *uu) { + int i; + + for (i = 0; i < 4; i++) { + uu[i] = (u & 0xff); + u >>= 8; + } +} + +static unsigned long +chars_to_ulong(unsigned char *uu) { + int i; + unsigned long u = 0; + + for (i = 3; i >= 0; i--) + u = (u << 8) | uu[i]; + return u; +} + +static int +save_sectors(char *dev, int fdin) { + struct sector *s; + char ss[516]; + int fdout = -1; + + fdout = open(save_sector_file, O_WRONLY | O_CREAT, 0444); + if (fdout < 0) { + warn(_("cannot open partition sector save file (%s)"), + save_sector_file); + goto err; + } + + for (s = sectorhead; s; s = s->next) + if (s->to_be_written) { + ulong_to_chars(s->sectornumber, ss); + if (!sseek(dev, fdin, s->sectornumber)) + goto err; + if (read(fdin, ss + 4, 512) != 512) { + warn(_("read error on %s - cannot read sector %llu"), + dev, s->sectornumber); + goto err; + } + if (write(fdout, ss, sizeof(ss)) != sizeof(ss)) { + warn(_("write error on %s"), save_sector_file); + goto err; + } + } + + if (close_fd(fdout) != 0) { + warn(_("write failed: %s"), save_sector_file); + return 0; + } + return 1; + + err: + if (fdout >= 0) + if (close_fd(fdout) != 0) + warn(_("write failed: %s"), save_sector_file); + return 0; +} + +static int reread_disk_partition(char *dev, int fd); + +static int +restore_sectors(char *dev) { + int fdin = -1, fdout = -1; + int ct; + struct stat statbuf; + char *ss0 = NULL, *ss; + unsigned long sno; + + if (stat(restore_sector_file, &statbuf) < 0) { + warn(_("cannot stat partition restore file (%s)"), + restore_sector_file); + goto err; + } + if (statbuf.st_size % 516) { + warnx(_("partition restore file has wrong size - not restoring")); + goto err; + } + + ss0 = xmalloc(statbuf.st_size); + ss = ss0; + + fdin = open(restore_sector_file, O_RDONLY); + if (fdin < 0) { + warn(_("cannot open partition restore file (%s)"), + restore_sector_file); + goto err; + } + if (read(fdin, ss, statbuf.st_size) != statbuf.st_size) { + warn(_("error reading %s"), restore_sector_file); + goto err; + } + + fdout = open(dev, O_WRONLY); + if (fdout < 0) { + warn(_("cannot open device %s for writing"), dev); + goto err; + } + + ct = statbuf.st_size / 516; + while (ct--) { + sno = chars_to_ulong((unsigned char *)ss); + if (!sseek(dev, fdout, sno)) + goto err; + if (write(fdout, ss + 4, 512) != 512) { + warn(_("error writing sector %lu on %s"), sno, dev); + goto err; + } + ss += 516; + } + free(ss0); + ss0 = NULL; + + if (!reread_disk_partition(dev, fdout)) /* closes fdout */ + goto err; + close(fdin); + if (close_fd(fdout) != 0) { + warnx(_("write failed: %s"), dev); + return 0; + } + return 1; + + err: + free(ss0); + if (fdin >= 0) + close(fdin); + if (fdout >= 0) + close(fdout); + + return 0; +} + +/* + * C. About heads, sectors and cylinders + */ + +/* + * defines HDIO_GETGEO and + * struct hd_geometry { + * unsigned char heads; + * unsigned char sectors; + * unsigned short cylinders; + * unsigned long start; + * }; + * + * For large disks g.cylinders is truncated, so we use BLKGETSIZE. + */ + +/* + * We consider several geometries for a disk: + * B - the BIOS geometry, gotten from the kernel via HDIO_GETGEO + * F - the fdisk geometry + * U - the user-specified geometry + * + * 0 means unspecified / unknown + */ +struct geometry { + unsigned long long total_size; /* in sectors */ + unsigned long cylindersize; /* in sectors */ + unsigned long heads, sectors, cylinders; + unsigned long start; +} B, F, U; + +static struct geometry +get_geometry(char *dev, int fd, int silent) { + struct hd_geometry g; + unsigned long cyls; + unsigned long long sectors; + struct geometry R; + +#ifdef HDIO_GETGEO + if (ioctl(fd, HDIO_GETGEO, &g)) +#endif + { + g.heads = g.sectors = g.cylinders = g.start = 0; + if (!silent) + warnx(_("Disk %s: cannot get geometry"), dev); + } + + R.start = g.start; + R.heads = g.heads; + R.sectors = g.sectors; + R.cylindersize = R.heads * R.sectors; + R.cylinders = 0; + R.total_size = 0; + + if (blkdev_get_sectors(fd, §ors) == -1) { + /* maybe an ordinary file */ + struct stat s; + + if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode)) + R.total_size = (s.st_size >> 9); + else if (!silent) + warnx(_("Disk %s: cannot get size"), dev); + } else + R.total_size = sectors; + + if (R.cylindersize && R.total_size) { + sectors /= R.cylindersize; + cyls = sectors; + if (cyls != sectors) + cyls = ~0; + R.cylinders = cyls; + } + + return R; +} + +static void +get_cylindersize(char *dev, int fd, int silent) { + struct geometry R; + + R = get_geometry(dev, fd, silent); + + B.heads = (U.heads ? U.heads : R.heads ? R.heads : 255); + B.sectors = (U.sectors ? U.sectors : R.sectors ? R.sectors : 63); + B.cylinders = (U.cylinders ? U.cylinders : R.cylinders); + + B.cylindersize = B.heads * B.sectors; + B.total_size = R.total_size; + + if (B.cylinders == 0 && B.cylindersize != 0) + B.cylinders = B.total_size / B.cylindersize; + + if (R.start && !force) { + warnx(_("Warning: start=%lu - this looks like a partition rather than\n" + "the entire disk. Using fdisk on it is probably meaningless.\n" + "[Use the --force option if you really want this]"), + R.start); + exit(EXIT_FAILURE); + } +#if 0 + if (R.heads && B.heads != R.heads) + warnx(_("Warning: HDIO_GETGEO says that there are %lu heads"), + R.heads); + if (R.sectors && B.sectors != R.sectors) + warnx(_("Warning: HDIO_GETGEO says that there are %lu sectors"), + R.sectors); + if (R.cylinders && B.cylinders != R.cylinders + && B.cylinders < 65536 && R.cylinders < 65536) + warnx(_("Warning: BLKGETSIZE/HDIO_GETGEO says that there are %lu cylinders"), + R.cylinders); +#endif + + if (B.sectors > 63) + warnx(_("Warning: unlikely number of sectors (%lu) - usually at most 63\n" + "This will give problems with all software that uses C/H/S addressing."), + B.sectors); + if (!silent) + printf(_("\nDisk %s: %lu cylinders, %lu heads, %lu sectors/track\n"), + dev, B.cylinders, B.heads, B.sectors); +} + +typedef struct { + unsigned char h, s, c; +} __attribute__ ((packed)) chs; /* has some c bits in s */ +chs zero_chs = { 0, 0, 0 }; + +typedef struct { + unsigned long h, s, c; +} longchs; +longchs zero_longchs; + +static chs +longchs_to_chs(longchs aa, struct geometry G) { + chs a; + + if (aa.h < 256 && aa.s < 64 && aa.c < 1024) { + a.h = aa.h; + a.s = aa.s | ((aa.c >> 2) & 0xc0); + a.c = (aa.c & 0xff); + } else if (G.heads && G.sectors) { + a.h = G.heads - 1; + a.s = G.sectors | 0xc0; + a.c = 0xff; + } else + a = zero_chs; + return a; +} + +static longchs +chs_to_longchs(chs a) { + longchs aa; + + aa.h = a.h; + aa.s = (a.s & 0x3f); + aa.c = (a.s & 0xc0); + aa.c = (aa.c << 2) + a.c; + return aa; +} + +static longchs +ulong_to_longchs(unsigned long sno, struct geometry G) { + longchs aa; + + if (G.heads && G.sectors && G.cylindersize) { + aa.s = 1 + sno % G.sectors; + aa.h = (sno / G.sectors) % G.heads; + aa.c = sno / G.cylindersize; + return aa; + } else { + return zero_longchs; + } +} + +static chs +ulong_to_chs(unsigned long sno, struct geometry G) { + return longchs_to_chs(ulong_to_longchs(sno, G), G); +} + +#if 0 +static unsigned long +longchs_to_ulong(longchs aa, struct geometry G) { + return (aa.c * G.cylindersize + aa.h * G.sectors + aa.s - 1); +} + +static unsigned long +chs_to_ulong(chs a, struct geometry G) { + return longchs_to_ulong(chs_to_longchs(a), G); +} +#endif + +static int +is_equal_chs(chs a, chs b) { + return (a.h == b.h && a.s == b.s && a.c == b.c); +} + +static int +chs_ok(chs a, char *v, char *w) { + longchs aa = chs_to_longchs(a); + int ret = 1; + + if (is_equal_chs(a, zero_chs)) + return 1; + if (B.heads && aa.h >= B.heads) { + warnx(_("%s of partition %s has impossible value for head: " + "%lu (should be in 0-%lu)"), w, v, aa.h, B.heads - 1); + ret = 0; + } + if (B.sectors && (aa.s == 0 || aa.s > B.sectors)) { + warnx(_("%s of partition %s has impossible value for sector: " + "%lu (should be in 1-%lu)"), w, v, aa.s, B.sectors); + ret = 0; + } + if (B.cylinders && aa.c >= B.cylinders) { + warnx(_("%s of partition %s has impossible value for cylinders: " + "%lu (should be in 0-%lu)"), w, v, aa.c, B.cylinders - 1); + ret = 0; + } + return ret; +} + +/* + * D. About system Ids + */ + +#define EMPTY_PARTITION 0 +#define EXTENDED_PARTITION 5 +#define WIN98_EXTENDED 0x0f +#define DM6_AUX1PARTITION 0x51 +#define DM6_AUX3PARTITION 0x53 +#define DM6_PARTITION 0x54 +#define EZD_PARTITION 0x55 +#define LINUX_SWAP 0x82 +#define LINUX_NATIVE 0x83 +#define LINUX_EXTENDED 0x85 +#define BSD_PARTITION 0xa5 +#define NETBSD_PARTITION 0xa9 + +/* List of partition types */ + +static const char * +sysname(unsigned char type) { + struct systypes *s; + + for (s = i386_sys_types; s->name; s++) + if (s->type == type) + return _(s->name); + return _("Unknown"); +} + +static void +list_types(void) { + struct systypes *s; + + printf(_("Id Name\n\n")); + for (s = i386_sys_types; s->name; s++) + printf("%2x %s\n", s->type, _(s->name)); +} + +static int +is_extended(unsigned char type) { + return (type == EXTENDED_PARTITION + || type == LINUX_EXTENDED || type == WIN98_EXTENDED); +} + +static int +is_bsd(unsigned char type) { + return (type == BSD_PARTITION || type == NETBSD_PARTITION); +} + +/* + * E. About partitions + */ + +/* MS/DOS partition */ + +struct partition { + unsigned char bootable; /* 0 or 0x80 */ + chs begin_chs; + unsigned char sys_type; + chs end_chs; + unsigned int start_sect; /* starting sector counting from 0 */ + unsigned int nr_sects; /* nr of sectors in partition */ +} __attribute__ ((packed)); + +/* Unfortunately, partitions are not aligned, and non-Intel machines + are unhappy with non-aligned integers. So, we need a copy by hand. */ +static int +copy_to_int(unsigned char *cp) { + unsigned int m; + + m = *cp++; + m += (*cp++ << 8); + m += (*cp++ << 16); + m += (*cp++ << 24); + return m; +} + +static void +copy_from_int(int m, char *cp) { + *cp++ = (m & 0xff); + m >>= 8; + *cp++ = (m & 0xff); + m >>= 8; + *cp++ = (m & 0xff); + m >>= 8; + *cp++ = (m & 0xff); +} + +static void +copy_to_part(char *cp, struct partition *p) { + p->bootable = *cp++; + p->begin_chs.h = *cp++; + p->begin_chs.s = *cp++; + p->begin_chs.c = *cp++; + p->sys_type = *cp++; + p->end_chs.h = *cp++; + p->end_chs.s = *cp++; + p->end_chs.c = *cp++; + p->start_sect = copy_to_int((unsigned char *)cp); + p->nr_sects = copy_to_int((unsigned char *)cp + 4); +} + +static void +copy_from_part(struct partition *p, char *cp) { + *cp++ = p->bootable; + *cp++ = p->begin_chs.h; + *cp++ = p->begin_chs.s; + *cp++ = p->begin_chs.c; + *cp++ = p->sys_type; + *cp++ = p->end_chs.h; + *cp++ = p->end_chs.s; + *cp++ = p->end_chs.c; + copy_from_int(p->start_sect, cp); + copy_from_int(p->nr_sects, cp + 4); +} + +/* Roughly speaking, Linux doesn't use any of the above fields except + for partition type, start sector and number of sectors. (However, + see also linux/drivers/scsi/fdomain.c.) + The only way partition type is used (in the kernel) is the comparison + for equality with EXTENDED_PARTITION (and these Disk Manager types). */ + +struct part_desc { + unsigned long long start; + unsigned long long size; + unsigned long long sector, offset; /* disk location of this info */ + struct partition p; + struct part_desc *ep; /* extended partition containing this one */ + int ptype; +#define DOS_TYPE 0 +#define BSD_TYPE 1 +} zero_part_desc; + +static struct part_desc * +outer_extended_partition(struct part_desc *p) { + while (p->ep) + p = p->ep; + return p; +} + +static int +is_parent(struct part_desc *pp, struct part_desc *p) { + while (p) { + if (pp == p) + return 1; + p = p->ep; + } + return 0; +} + +struct disk_desc { + struct part_desc partitions[512]; + int partno; +} oldp, newp; + +/* determine where on the disk this information goes */ +static void +add_sector_and_offset(struct disk_desc *z) { + int pno; + struct part_desc *p; + + for (pno = 0; pno < z->partno; pno++) { + p = &(z->partitions[pno]); + p->offset = 0x1be + (pno % 4) * sizeof(struct partition); + p->sector = (p->ep ? p->ep->start : 0); + } +} + +/* tell the kernel to reread the partition tables */ +static int +reread_ioctl(int fd) { +#ifdef BLKRRPART + if (ioctl(fd, BLKRRPART)) +#else + errno = ENOSYS; +#endif + { + /* perror might change errno */ + int err = errno; + + warn("BLKRRPART"); + + /* 2.6.8 returns EIO for a zero table */ + if (err == EBUSY) + return -1; + } + return 0; +} + +/* reread after writing */ +static int +reread_disk_partition(char *dev, int fd) { + fflush(stdout); + sync(); + + if (is_blkdev(fd)) { + printf(_("Re-reading the partition table ...\n")); + if (reread_ioctl(fd) ) { + warnx(_("The command to re-read the partition table failed.\n" + "Run partprobe(8), kpartx(8) or reboot your system now,\n" + "before using mkfs")); + return 0; + } + } + + if (close_fd(fd) != 0) { + warn(_("Error closing %s"), dev); + return 0; + } + printf("\n"); + + return 1; +} + +/* find Linux name of this partition, assuming that it will have a name */ +static int +index_to_linux(int pno, struct disk_desc *z) { + int i, ct = 1; + struct part_desc *p = &(z->partitions[0]); + for (i = 0; i < pno; i++, p++) + if (i < 4 || (p->size > 0 && !is_extended(p->p.sys_type))) + ct++; + return ct; +} + +static int +linux_to_index(int lpno, struct disk_desc *z) { + int i, ct = 0; + struct part_desc *p = &(z->partitions[0]); + for (i = 0; i < z->partno && ct < lpno; i++, p++) + if ((i < 4 || (p->size > 0 && !is_extended(p->p.sys_type))) + && ++ct == lpno) + return i; + return -1; +} + +static int +asc_to_index(char *pnam, struct disk_desc *z) { + int pnum, pno; + + if (*pnam == '#') { + pno = atoi(pnam + 1); + } else { + pnum = atoi(pnam); + pno = linux_to_index(pnum, z); + } + if (!(pno >= 0 && pno < z->partno)) + errx(EXIT_FAILURE, _("%s: no such partition\n"), pnam); + return pno; +} + +/* + * List partitions - in terms of sectors, blocks or cylinders + */ +#define F_SECTOR 1 +#define F_BLOCK 2 +#define F_CYLINDER 3 +#define F_MEGABYTE 4 + +int default_format = F_MEGABYTE; +int specified_format = 0; +int show_extended = 0; +int one_only = 0; +int one_only_pno; +int increment = 0; + +static void +set_format(char c) { + switch (c) { + default: + warnx(_("unrecognized format - using sectors")); + /* fallthrough */ + case 'S': + specified_format = F_SECTOR; + break; + case 'B': + specified_format = F_BLOCK; + break; + case 'C': + specified_format = F_CYLINDER; + break; + case 'M': + specified_format = F_MEGABYTE; + break; + } +} + +static unsigned long +unitsize(int format) { + default_format = (B.cylindersize ? F_CYLINDER : F_MEGABYTE); + if (!format && !(format = specified_format)) + format = default_format; + + switch (format) { + default: + case F_CYLINDER: + if (B.cylindersize) + return B.cylindersize; + /* fallthrough */ + case F_SECTOR: + return 1; + case F_BLOCK: + return 2; + case F_MEGABYTE: + return 2048; + } +} + +static unsigned long long +get_disksize(int format) { + if (B.total_size && leave_last) + /* don't use last cylinder (--leave-last option) */ + return (B.total_size - B.cylindersize) / unitsize(format); + + return B.total_size / unitsize(format); +} + +static void +out_partition_header(char *dev, int format, struct geometry G) { + if (dump) { + printf("# partition table of %s\n", dev); + printf("unit: sectors\n\n"); + return; + } + + default_format = (G.cylindersize ? F_CYLINDER : F_MEGABYTE); + if (!format && !(format = specified_format)) + format = default_format; + + switch (format) { + default: + warnx(_("unimplemented format - using %s"), + G.cylindersize ? _("cylinders") : _("sectors")); + /* fallthrough */ + case F_CYLINDER: + if (G.cylindersize) { + printf(_("Units: cylinders of %lu bytes, blocks of 1024 bytes" + ", counting from %d\n\n"), G.cylindersize << 9, increment); + printf(_(" Device Boot Start End #cyls #blocks Id System\n")); + break; + } + /* fall through */ + case F_SECTOR: + printf(_("Units: sectors of 512 bytes, counting from %d\n\n"), + increment); + printf(_(" Device Boot Start End #sectors Id System\n")); + break; + case F_BLOCK: + printf(_("Units: blocks of 1024 bytes, counting from %d\n\n"), + increment); + printf(_(" Device Boot Start End #blocks Id System\n")); + break; + case F_MEGABYTE: + printf(_("Units: 1MiB = 1024*1024 bytes, blocks of 1024 bytes" + ", counting from %d\n\n"), increment); + printf(_(" Device Boot Start End MiB #blocks Id System\n")); + break; + } +} + +static void +out_rounddown(int width, unsigned long long n, unsigned long unit, int inc) { + printf("%*llu", width, inc + n / unit); + if (unit != 1) + putchar((n % unit) ? '+' : ' '); + putchar(' '); +} + +static void +out_roundup(int width, unsigned long long n, unsigned long unit, int inc) { + if (n == (unsigned long long)(-1)) + printf("%*s", width, "-"); + else + printf("%*llu", width, inc + n / unit); + if (unit != 1) + putchar(((n + 1) % unit) ? '-' : ' '); + putchar(' '); +} + +static void +out_roundup_size(int width, unsigned long long n, unsigned long unit) { + printf("%*llu", width, (n + unit - 1) / unit); + if (unit != 1) + putchar((n % unit) ? '-' : ' '); + putchar(' '); +} + +static struct geometry +get_fdisk_geometry_one(struct part_desc *p) { + struct geometry G; + chs b = p->p.end_chs; + longchs bb = chs_to_longchs(b); + + memset(&G, 0, sizeof(struct geometry)); + G.heads = bb.h + 1; + G.sectors = bb.s; + G.cylindersize = G.heads * G.sectors; + return G; +} + +static int +get_fdisk_geometry(struct disk_desc *z) { + struct part_desc *p; + int pno, agree; + struct geometry G0, G; + + memset(&G0, 0, sizeof(struct geometry)); + agree = 0; + for (pno = 0; pno < z->partno; pno++) { + p = &(z->partitions[pno]); + if (p->size != 0 && p->p.sys_type != 0) { + G = get_fdisk_geometry_one(p); + if (!G0.heads) { + G0 = G; + agree = 1; + } else if (G.heads != G0.heads || G.sectors != G0.sectors) { + agree = 0; + break; + } + } + } + F = (agree ? G0 : B); + return (F.sectors != B.sectors || F.heads != B.heads); +} + +static void +out_partition(char *dev, int format, struct part_desc *p, + struct disk_desc *z, struct geometry G) { + unsigned long long start, end, size; + int pno, lpno; + + if (!format && !(format = specified_format)) + format = default_format; + + pno = p - &(z->partitions[0]); /* our index */ + lpno = index_to_linux(pno, z); /* name of next one that has a name */ + if (pno == linux_to_index(lpno, z)) /* was that us? */ + printf("%s", partname(dev, lpno, 10)); /* yes */ + else if (show_extended) + printf(" - "); + else + return; + putchar(dump ? ':' : ' '); + + start = p->start; + end = p->start + p->size - 1; + size = p->size; + + if (dump) { + printf(" start=%9llu", start); + printf(", size=%9llu", size); + if (p->ptype == DOS_TYPE) { + printf(", Id=%2x", p->p.sys_type); + if (p->p.bootable == 0x80) + printf(", bootable"); + } + printf("\n"); + return; + } + + if (p->ptype != DOS_TYPE || p->p.bootable == 0) + printf(" "); + else if (p->p.bootable == 0x80) + printf(" * "); + else + printf(" ? "); /* garbage */ + + switch (format) { + case F_CYLINDER: + if (G.cylindersize) { + out_rounddown(6, start, G.cylindersize, increment); + out_roundup(6, end, G.cylindersize, increment); + out_roundup_size(6, size, G.cylindersize); + out_rounddown(9, size, 2, 0); + break; + } + /* fall through */ + default: + case F_SECTOR: + out_rounddown(9, start, 1, increment); + out_roundup(9, end, 1, increment); + out_rounddown(10, size, 1, 0); + break; + case F_BLOCK: +#if 0 + printf("%8lu,%3lu ", + p->sector / 2, ((p->sector & 1) ? 512 : 0) + p->offset); +#endif + out_rounddown(8, start, 2, increment); + out_roundup(8, end, 2, increment); + out_rounddown(9, size, 2, 0); + break; + case F_MEGABYTE: + out_rounddown(5, start, 2048, increment); + out_roundup(5, end, 2048, increment); + out_roundup_size(5, size, 2048); + out_rounddown(9, size, 2, 0); + break; + } + if (p->ptype == DOS_TYPE) { + printf(" %2x %s\n", p->p.sys_type, sysname(p->p.sys_type)); + } else { + printf("\n"); + } + + /* Is chs as we expect? */ + if (!quiet && p->ptype == DOS_TYPE) { + chs a, b; + longchs aa, bb; + a = (size ? ulong_to_chs(start, G) : zero_chs); + b = p->p.begin_chs; + aa = chs_to_longchs(a); + bb = chs_to_longchs(b); + if (a.s && !is_equal_chs(a, b)) + printf(_("\t\tstart: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"), + aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); + a = (size ? ulong_to_chs(end, G) : zero_chs); + b = p->p.end_chs; + aa = chs_to_longchs(a); + bb = chs_to_longchs(b); + if (a.s && !is_equal_chs(a, b)) + printf(_("\t\tend: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"), + aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); + if (G.cylinders && G.cylinders < 1024 && bb.c > G.cylinders) + printf(_("partition ends on cylinder %ld, beyond the end of the disk\n"), + bb.c); + } +} + +static void +out_partitions(char *dev, struct disk_desc *z) { + int pno, format = 0; + + if (z->partno == 0) { + if (!opt_list) + warnx(_("No partitions found")); + } else { + if (get_fdisk_geometry(z) && !dump) { + warnx(_("Warning: The partition table looks like it was made\n" + " for C/H/S=*/%ld/%ld (instead of %ld/%ld/%ld).\n" + "For this listing I'll assume that geometry."), + F.heads, F.sectors, B.cylinders, B.heads, B.sectors); + } + + out_partition_header(dev, format, F); + for (pno = 0; pno < z->partno; pno++) { + out_partition(dev, format, &(z->partitions[pno]), z, F); + if (show_extended && pno % 4 == 3) + printf("\n"); + } + } +} + +static int +disj(struct part_desc *p, struct part_desc *q) { + return ((p->start + p->size <= q->start) + || (is_extended(p->p.sys_type) + && q->start + q->size <= p->start + p->size)); +} + +static char * +pnumber(struct part_desc *p, struct disk_desc *z) { + static char buf[20]; + int this, next; + struct part_desc *p0 = &(z->partitions[0]); + + this = index_to_linux(p - p0, z); + next = index_to_linux(p - p0 + 1, z); + + if (next > this) + sprintf(buf, "%d", this); + else + sprintf(buf, "[%d]", this); + return buf; +} + +static int +partitions_ok(int fd, struct disk_desc *z) { + struct part_desc *partitions = &(z->partitions[0]), *p, *q; + int partno = z->partno; + int sector_size; + +#define PNO(p) pnumber(p, z) + + /* Have at least 4 partitions been defined? */ + if (partno < 4) { + if (!partno) + errx(EXIT_FAILURE, _("no partition table present.")); + else + errx(EXIT_FAILURE, _("strange, only %d partitions defined."), partno); + return 0; + } + + /* Are the partitions of size 0 marked empty? + And do they have start = 0? And bootable = 0? */ + for (p = partitions; p - partitions < partno; p++) + if (p->size == 0) { + if (p->p.sys_type != EMPTY_PARTITION) + warnx(_("Warning: partition %s has size 0 but is not marked Empty"), + PNO(p)); + else if (p->p.bootable != 0) + warnx(_("Warning: partition %s has size 0 and is bootable"), + PNO(p)); + else if (p->p.start_sect != 0) + warnx(_("Warning: partition %s has size 0 and nonzero start"), + PNO(p)); + /* all this is probably harmless, no error return */ + } + + /* Are the logical partitions contained in their extended partitions? */ + for (p = partitions + 4; p < partitions + partno; p++) + if (p->ptype == DOS_TYPE) + if (p->size && !is_extended(p->p.sys_type)) { + q = p->ep; + if (p->start < q->start + || p->start + p->size > q->start + q->size) { + warnx(_("Warning: partition %s is not contained in " + "partition %s"), PNO(p), PNO(q)); + return 0; + } + } + + /* Are the data partitions mutually disjoint? */ + for (p = partitions; p < partitions + partno; p++) + if (p->size && !is_extended(p->p.sys_type)) + for (q = p + 1; q < partitions + partno; q++) + if (q->size && !is_extended(q->p.sys_type)) + if (!((p->start > q->start) ? disj(q, p) : disj(p, q))) { + warnx(_("Warning: partitions %s and %s overlap"), + PNO(p), PNO(q)); + return 0; + } + + /* Are the data partitions and the extended partition + table sectors disjoint? */ + for (p = partitions; p < partitions + partno; p++) + if (p->size && !is_extended(p->p.sys_type)) + for (q = partitions; q < partitions + partno; q++) + if (is_extended(q->p.sys_type)) + if (p->start <= q->start && p->start + p->size > q->start) { + warnx(_("Warning: partition %s contains part of " + "the partition table (sector %llu),\n" + "and will destroy it when filled"), + PNO(p), q->start); + return 0; + } + + /* Do they start past zero and end before end-of-disk? */ + { + unsigned long long ds = get_disksize(F_SECTOR); + for (p = partitions; p < partitions + partno; p++) + if (p->size) { + if (p->start == 0) { + warnx(_("Warning: partition %s starts at sector 0"), + PNO(p)); + return 0; + } + if (p->size && p->start + p->size > ds) { + warnx(_("Warning: partition %s extends past end of disk"), + PNO(p)); + return 0; + } + } + } + + if (blkdev_get_sector_size(fd, §or_size) == -1) + sector_size = DEFAULT_SECTOR_SIZE; + + /* Is the size of partitions less than 2^32 sectors limit? */ + for (p = partitions; p < partitions + partno; p++) + if (p->size > UINT_MAX) { + unsigned long long bytes = p->size * sector_size; + int giga = bytes / 1000000000; + int hectogiga = (giga + 50) / 100; + warnx(_("Warning: partition %s has size %d.%d TB (%llu bytes),\n" + "which is larger than the %llu bytes limit imposed\n" + "by the DOS partition table for %d-byte sectors"), + PNO(p), hectogiga / 10, hectogiga % 10, + bytes, + (unsigned long long) UINT_MAX * sector_size, + sector_size); + return 0; + } + + /* Do the partitions start below the 2^32 sectors limit? */ + for (p = partitions; p < partitions + partno; p++) + if (p->start > UINT_MAX) { + unsigned long long bytes = p->start * sector_size; + int giga = bytes / 1000000000; + int hectogiga = (giga + 50) / 100; + warnx(_("Warning: partition %s starts at sector %llu (%d.%d TB for %d-byte sectors),\n" + "which exceeds the DOS partition table limit of %llu sectors"), + PNO(p), + p->start, + hectogiga / 10, + hectogiga % 10, + sector_size, + (unsigned long long) UINT_MAX); + return 0; + } + + /* At most one chain of DOS extended partitions ? */ + /* It seems that the OS/2 fdisk has the additional requirement + that the extended partition must be the fourth one */ + { + int ect = 0; + for (p = partitions; p < partitions + 4; p++) + if (p->p.sys_type == EXTENDED_PARTITION) + ect++; + if (ect > 1 && !Linux) { + warnx(_("Among the primary partitions, at most one can be extended\n" + " (although this is not a problem under Linux)")); + return 0; + } + } + + /* + * Do all partitions start at a cylinder boundary ? + * (this is not required for Linux) + * The first partition starts after MBR. + * Logical partitions start slightly after the containing extended partn. + */ + if (B.cylindersize && !Linux) { + for (p = partitions; p < partitions + partno; p++) + if (p->size) { + if (p->start % B.cylindersize != 0 + && (!p->ep + || p->start / B.cylindersize != + p->ep->start / B.cylindersize) + && (p->p.start_sect >= B.cylindersize)) { + warnx(_("Warning: partition %s does not start " + "at a cylinder boundary"), PNO(p)); + if (specified_format == F_CYLINDER) + return 0; + } + if ((p->start + p->size) % B.cylindersize) { + warnx(_("Warning: partition %s does not end " + "at a cylinder boundary"), PNO(p)); + if (specified_format == F_CYLINDER) + return 0; + } + } + } + + /* Usually, one can boot only from primary partitions. */ + /* In fact, from a unique one only. */ + /* do not warn about bootable extended partitions - + often LILO is there */ + { + int pno = -1; + for (p = partitions; p < partitions + partno; p++) + if (p->p.bootable) { + if (pno == -1) + pno = p - partitions; + else if (p - partitions < 4) { + warnx(_("Warning: more than one primary partition is marked " + "bootable (active)\n" + "This does not matter for LILO, but the DOS MBR will " + "not boot this disk.")); + break; + } + if (p - partitions >= 4) { + warnx(_("Warning: usually one can boot from primary partitions " + "only\nLILO disregards the `bootable' flag.")); + break; + } + } + if (pno == -1 || pno >= 4) + warnx(_("Warning: no primary partition is marked bootable (active)\n" + "This does not matter for LILO, but the DOS MBR will " + "not boot this disk.")); + } + + /* Is chs as we expect? */ + for (p = partitions; p < partitions + partno; p++) + if (p->ptype == DOS_TYPE) { + chs a, b; + longchs aa, bb; + a = p->size ? ulong_to_chs(p->start, B) : zero_chs; + b = p->p.begin_chs; + aa = chs_to_longchs(a); + bb = chs_to_longchs(b); + if (!Linux && !chs_ok(b, PNO(p), _("start"))) + return 0; + if (a.s && !is_equal_chs(a, b)) + warnx(_("partition %s: start: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)"), + PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); + a = p->size ? ulong_to_chs(p->start + p->size - 1, B) : zero_chs; + b = p->p.end_chs; + aa = chs_to_longchs(a); + bb = chs_to_longchs(b); + if (!Linux && !chs_ok(b, PNO(p), _("end"))) + return 0; + if (a.s && !is_equal_chs(a, b)) + warnx(_("partition %s: end: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)"), + PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); + if (B.cylinders && B.cylinders < 1024 && bb.c > B.cylinders) + warnx(_("partition %s ends on cylinder %ld, beyond the end of the disk"), + PNO(p), bb.c); + } + + return 1; + +#undef PNO +} + +static void +extended_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) { + char *cp; + struct sector *s; + unsigned long long start, here, next; + int i, moretodo = 1; + struct partition p; + struct part_desc *partitions = &(z->partitions[0]); + size_t pno = z->partno; + + here = start = ep->start; + + if (B.cylindersize && start % B.cylindersize) { + /* This is BAD */ + if (DOS_extended) { + here = start -= (start % B.cylindersize); + warnx(_("Warning: shifted start of the extd partition " + "from %lld to %lld\n" + "(For listing purposes only. " + "Do not change its contents.)"), ep->start, start); + } else { + warnx(_("Warning: extended partition does not start at a " + "cylinder boundary.\n" + "DOS and Linux will interpret the contents differently.")); + } + } + + while (moretodo) { + moretodo = 0; + + if (!(s = get_sector(dev, fd, here))) + break; + + if (!msdos_signature(s)) { + warnx(_("ERROR: sector %llu does not have an msdos signature"), + s->sectornumber); + break; + } + cp = s->data + 0x1be; + + if (pno + 4 >= ARRAY_SIZE(z->partitions)) { + warnx(_("too many partitions - ignoring those past nr (%zu)"), + pno - 1); + break; + } + + next = 0; + + for (i = 0; i < 4; i++, cp += sizeof(struct partition)) { + partitions[pno].sector = here; + partitions[pno].offset = cp - s->data; + partitions[pno].ep = ep; + copy_to_part(cp, &p); + if (is_extended(p.sys_type)) { + partitions[pno].start = start + p.start_sect; + if (next) + warnx(_("tree of partitions?")); + else + next = partitions[pno].start; /* follow `upper' branch */ + moretodo = 1; + } else { + partitions[pno].start = here + p.start_sect; + } + partitions[pno].size = p.nr_sects; + partitions[pno].ptype = DOS_TYPE; + partitions[pno].p = p; + pno++; + } + here = next; + } + + z->partno = pno; +} + +#define BSD_DISKMAGIC (0x82564557UL) +#define BSD_MAXPARTITIONS 16 +#define BSD_FS_UNUSED 0 +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +struct bsd_disklabel { + u32 d_magic; + char d_junk1[4]; + char d_typename[16]; + char d_packname[16]; + char d_junk2[92]; + u32 d_magic2; + char d_junk3[2]; + u16 d_npartitions; /* number of partitions in following */ + char d_junk4[8]; + struct bsd_partition { /* the partition table */ + u32 p_size; /* number of sectors in partition */ + u32 p_offset; /* starting sector */ + u32 p_fsize; /* filesystem basic fragment size */ + u8 p_fstype; /* filesystem type, see below */ + u8 p_frag; /* filesystem fragments per block */ + u16 p_cpg; /* filesystem cylinders per group */ + } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ +}; + +static void +bsd_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) { + struct bsd_disklabel *l; + struct bsd_partition *bp, *bp0; + unsigned long long start = ep->start; + struct sector *s; + struct part_desc *partitions = &(z->partitions[0]); + size_t pno = z->partno; + + if (!(s = get_sector(dev, fd, start + 1))) + return; + l = (struct bsd_disklabel *)(s->data); + if (l->d_magic != BSD_DISKMAGIC || l->d_magic2 != BSD_DISKMAGIC) + return; + + bp = bp0 = &l->d_partitions[0]; + while (bp - bp0 < BSD_MAXPARTITIONS && bp - bp0 < l->d_npartitions) { + if (pno + 1 >= ARRAY_SIZE(z->partitions)) { + warnx(_("too many partitions - ignoring those " + "past nr (%zu)"), pno - 1); + break; + } + if (bp->p_fstype != BSD_FS_UNUSED) { + partitions[pno].start = bp->p_offset; + partitions[pno].size = bp->p_size; + partitions[pno].sector = start + 1; + partitions[pno].offset = (char *)bp - (char *)bp0; + partitions[pno].ep = 0; + partitions[pno].ptype = BSD_TYPE; + pno++; + } + bp++; + } + z->partno = pno; +} + +static int +msdos_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) { + int i; + char *cp; + struct partition pt; + struct sector *s; + struct part_desc *partitions = &(z->partitions[0]); + int pno; + int bsd_later = 1; + unsigned short sig, magic; +#ifdef __linux__ + bsd_later = (get_linux_version() >= KERNEL_VERSION(2, 3, 40)); +#endif + + if (!(s = get_sector(dev, fd, start))) + return 0; + + if (!msdos_signature(s)) + return 0; + + cp = s->data + 0x1be; + copy_to_part(cp, &pt); + + /* If I am not mistaken, recent kernels will hide this from us, + so we will never actually see traces of a Disk Manager */ + if (pt.sys_type == DM6_PARTITION + || pt.sys_type == EZD_PARTITION + || pt.sys_type == DM6_AUX1PARTITION + || pt.sys_type == DM6_AUX3PARTITION) { + warnx(_("detected Disk Manager - unable to handle that")); + return 0; + } + + memcpy(&sig, s->data + 2, sizeof(sig)); + if (sig <= 0x1ae) { + memcpy(&magic, s->data + sig, sizeof(magic)); + if (magic == 0x55aa && (1 & *(unsigned char *)(s->data + sig + 2))) { + warnx(_("DM6 signature found - giving up")); + return 0; + } + } + + for (pno = 0; pno < 4; pno++, cp += sizeof(struct partition)) { + partitions[pno].sector = start; + partitions[pno].offset = cp - s->data; + copy_to_part(cp, &pt); + partitions[pno].start = start + pt.start_sect; + partitions[pno].size = pt.nr_sects; + partitions[pno].ep = 0; + partitions[pno].p = pt; + } + + z->partno = pno; + + for (i = 0; i < 4; i++) { + if (is_extended(partitions[i].p.sys_type)) { + if (!partitions[i].size) { + warnx(_("strange..., an extended partition of size 0?")); + continue; + } + extended_partition(dev, fd, &partitions[i], z); + } + if (!bsd_later && is_bsd(partitions[i].p.sys_type)) { + if (!partitions[i].size) { + warnx(_("strange..., a BSD partition of size 0?")); + continue; + } + bsd_partition(dev, fd, &partitions[i], z); + } + } + + if (bsd_later) { + for (i = 0; i < 4; i++) { + if (is_bsd(partitions[i].p.sys_type)) { + if (!partitions[i].size) { + warnx(_("strange..., a BSD partition of size 0?")); + continue; + } + bsd_partition(dev, fd, &partitions[i], z); + } + } + } + + return 1; +} + +static int +osf_partition(char *dev __attribute__ ((__unused__)), + int fd __attribute__ ((__unused__)), + unsigned long start __attribute__ ((__unused__)), + struct disk_desc *z __attribute__ ((__unused__))) { + return 0; +} + +static int +sun_partition(char *dev __attribute__ ((__unused__)), + int fd __attribute__ ((__unused__)), + unsigned long start __attribute__ ((__unused__)), + struct disk_desc *z __attribute__ ((__unused__))) { + return 0; +} + +static int +amiga_partition(char *dev __attribute__ ((__unused__)), + int fd __attribute__ ((__unused__)), + unsigned long start __attribute__ ((__unused__)), + struct disk_desc *z __attribute__ ((__unused__))) { + return 0; +} + +static void +get_partitions(char *dev, int fd, struct disk_desc *z) { + z->partno = 0; + + if (!msdos_partition(dev, fd, 0, z) + && !osf_partition(dev, fd, 0, z) + && !sun_partition(dev, fd, 0, z) + && !amiga_partition(dev, fd, 0, z)) { + if (!opt_list) + warnx(_(" %s: unrecognized partition table type"), dev); + return; + } +} + +static int +write_partitions(char *dev, int fd, struct disk_desc *z) { + struct sector *s; + struct part_desc *partitions = &(z->partitions[0]), *p; + int pno = z->partno; + + if (no_write) { + warnx(_("-n flag was given: Nothing changed")); + exit(EXIT_SUCCESS); + } + + for (p = partitions; p < partitions + pno; p++) { + s = get_sector(dev, fd, p->sector); + if (!s) + return 0; + s->to_be_written = 1; + if (p->ptype == DOS_TYPE) { + copy_from_part(&(p->p), s->data + p->offset); + s->data[510] = 0x55; + s->data[511] = (unsigned char)0xaa; + } + } + if (save_sector_file) { + if (!save_sectors(dev, fd)) { + errx(EXIT_FAILURE, _("Failed saving the old sectors - aborting\n")); + return 0; + } + } + if (!write_sectors(dev, fd)) { + warnx(_("Failed writing the partition on %s"), dev); + return 0; + } + if (fsync(fd)) { + warn(_("Failed writing the partition on %s"), dev); + return 0; + } + return 1; +} + +/* + * F. The standard input + */ + +/* + * Input format: + * + * Fields are separated by whitespace or comma or semicolon possibly + * followed by whitespace; initial and trailing whitespace is ignored. + * Numbers can be octal, decimal or hexadecimal, decimal is default + * The parts can (and probably should) be omitted. + * Bootable is specified as [*|-], with as default not-bootable. + * Type is given in hex, without the 0x prefix, or is [E|S|L|X], where + * L (LINUX_NATIVE (83)) is the default, S is LINUX_SWAP (82), and E + * is EXTENDED_PARTITION (5), X is LINUX_EXTENDED (85). + * The default value of start is the first nonassigned sector/cylinder/... + * The default value of size is as much as possible (until next + * partition or end-of-disk). + * .: end of chain of extended partitions. + * + * On interactive input an empty line means: all defaults. + * Otherwise empty lines are ignored. + */ + +int eof, eob; + +struct dumpfld { + int fldno; + char *fldname; + int is_bool; +} dumpflds[] = { + { + 0, "start", 0}, { + 1, "size", 0}, { + 2, "Id", 0}, { + 3, "bootable", 1}, { + 4, "bh", 0}, { + 5, "bs", 0}, { + 6, "bc", 0}, { + 7, "eh", 0}, { + 8, "es", 0}, { + 9, "ec", 0} +}; + +/* + * Read a line, split it into fields + * + * (some primitive handwork, but a more elaborate parser seems + * unnecessary) + */ +#define RD_EOF (-1) +#define RD_CMD (-2) + +static int +read_stdin(char **fields, char *line, int fieldssize, int linesize) { + char *lp, *ip; + int c, fno; + + /* boolean true and empty string at start */ + line[0] = '*'; + line[1] = 0; + for (fno = 0; fno < fieldssize; fno++) + fields[fno] = line + 1; + fno = 0; + + /* read a line from stdin */ + lp = fgets(line + 2, linesize - 2, stdin); + if (lp == NULL) { + eof = 1; + return RD_EOF; + } + if (!(lp = strchr(lp, '\n'))) + errx(EXIT_FAILURE, _("long or incomplete input line - quitting")); + *lp = 0; + + /* remove comments, if any */ + if ((lp = strchr(line + 2, '#')) != 0) + *lp = 0; + + /* recognize a few commands - to be expanded */ + if (!strcmp(line + 2, "unit: sectors")) { + specified_format = F_SECTOR; + return RD_CMD; + } + + /* dump style? - then bad input is fatal */ + if ((ip = strchr(line + 2, ':')) != 0) { + struct dumpfld *d; + + nxtfld: + ip++; + while (isspace(*ip)) + ip++; + if (*ip == 0) + return fno; + for (d = dumpflds; (size_t) (d - dumpflds) < ARRAY_SIZE(dumpflds); d++) { + if (!strncmp(ip, d->fldname, strlen(d->fldname))) { + ip += strlen(d->fldname); + while (isspace(*ip)) + ip++; + if (d->is_bool) + fields[d->fldno] = line; + else if (*ip == '=') { + while (isspace(*++ip)) ; + fields[d->fldno] = ip; + while (isalnum(*ip)) /* 0x07FF */ + ip++; + } else + errx(EXIT_FAILURE, _("input error: `=' expected after %s field"), + d->fldname); + if (fno <= d->fldno) + fno = d->fldno + 1; + if (*ip == 0) + return fno; + if (*ip != ',' && *ip != ';') + errx(EXIT_FAILURE, _("input error: unexpected character %c after %s field"), + *ip, d->fldname); + *ip = 0; + goto nxtfld; + } + } + errx(EXIT_FAILURE, _("unrecognized input: %s"), ip); + } + + /* split line into fields */ + lp = ip = line + 2; + fields[fno++] = lp; + while ((c = *ip++) != 0) { + if (!lp[-1] && (c == '\t' || c == ' ')) ; + else if (c == '\t' || c == ' ' || c == ',' || c == ';') { + *lp++ = 0; + if (fno < fieldssize) + fields[fno++] = lp; + continue; + } else + *lp++ = c; + } + + if (lp == fields[fno - 1]) + fno--; + return fno; +} + +/* read a number, use default if absent */ +/* a sign gives an offset from the default */ +static int +get_ul(char *u, unsigned long *up, unsigned long def, int base) { + char *nu; + int sign = 0; + unsigned long val; + + if (*u == '+') { + sign = 1; + u++; + } else if (*u == '-') { + sign = -1; + u++; + } + if (*u) { + errno = 0; + val = strtoul(u, &nu, base); + if (errno == ERANGE) { + warnx(_("number too big")); + return -1; + } + if (*nu) { + warnx(_("trailing junk after number")); + return -1; + } + if (sign == 1) + val = def + val; + else if (sign == -1) + val = def - val; + *up = val; + } else + *up = def; + return 0; +} + + +/* read a number, use default if absent */ +/* a sign gives an offset from the default */ +static int +get_ull(char *u, unsigned long long *up, unsigned long long def, int base) { + char *nu; + int sign = 0; + unsigned long long val; + + if (*u == '+') { + sign = 1; + u++; + } else if (*u == '-') { + sign = -1; + u++; + } + if (*u) { + errno = 0; + val = strtoull(u, &nu, base); + if (errno == ERANGE) { + warnx(_("number too big")); + return -1; + } + if (*nu) { + warnx(_("trailing junk after number")); + return -1; + } + if (sign == 1) + val = def + val; + else if (sign == -1) + val = def - val; + *up = val; + } else + *up = def; + return 0; +} + + +/* There are two common ways to structure extended partitions: + as nested boxes, and as a chain. Sometimes the partitions + must be given in order. Sometimes all logical partitions + must lie inside the outermost extended partition. +NESTED: every partition is contained in the surrounding partitions + and is disjoint from all others. +CHAINED: every data partition is contained in the surrounding partitions + and disjoint from all others, but extended partitions may lie outside + (insofar as allowed by all_logicals_inside_outermost_extended). +ONESECTOR: all data partitions are mutually disjoint; extended partitions + each use one sector only (except perhaps for the outermost one). +*/ +int partitions_in_order = 0; +int all_logicals_inside_outermost_extended = 1; +enum { NESTED, CHAINED, ONESECTOR } boxes = NESTED; + +/* find the default value for - assuming entire units */ +static unsigned long long +first_free(int pno, int is_extended, struct part_desc *ep, int format, + unsigned long long mid, struct disk_desc *z) { + unsigned long long ff, fff; + unsigned long unit = unitsize(format); + struct part_desc *partitions = &(z->partitions[0]), *pp = 0; + + /* if containing ep undefined, look at its container */ + if (ep && ep->p.sys_type == EMPTY_PARTITION) + ep = ep->ep; + + if (ep) { + if (boxes == NESTED || (boxes == CHAINED && !is_extended)) + pp = ep; + else if (all_logicals_inside_outermost_extended) + pp = outer_extended_partition(ep); + } +#if 0 + ff = pp ? (pp->start + unit - 1) / unit : 0; +#else + /* rounding up wastes almost an entire cylinder - round down + and leave it to compute_start_sect() to fix the difference */ + ff = pp ? pp->start / unit : 0; +#endif + /* MBR and 1st sector of an extended partition are never free */ + if (unit == 1) + ff++; + + again: + for (pp = partitions; pp < partitions + pno; pp++) { + if (!is_parent(pp, ep) && pp->size > 0) { + if ((partitions_in_order || pp->start / unit <= ff + || (mid && pp->start / unit <= mid)) + && (fff = (pp->start + pp->size + unit - 1) / unit) > ff) { + ff = fff; + goto again; + } + } + } + + return ff; +} + +/* find the default value for - assuming entire units */ +static unsigned long long +max_length(int pno, int is_extended, struct part_desc *ep, int format, + unsigned long long start, struct disk_desc *z) { + unsigned long long fu; + unsigned long unit = unitsize(format); + struct part_desc *partitions = &(z->partitions[0]), *pp = 0; + + /* if containing ep undefined, look at its container */ + if (ep && ep->p.sys_type == EMPTY_PARTITION) + ep = ep->ep; + + if (ep) { + if (boxes == NESTED || (boxes == CHAINED && !is_extended)) + pp = ep; + else if (all_logicals_inside_outermost_extended) + pp = outer_extended_partition(ep); + } + fu = pp ? (pp->start + pp->size) / unit : get_disksize(format); + + for (pp = partitions; pp < partitions + pno; pp++) + if (!is_parent(pp, ep) && pp->size > 0 + && pp->start / unit >= start && pp->start / unit < fu) + fu = pp->start / unit; + + return (fu > start) ? fu - start : 0; +} + +/* compute starting sector of a partition inside an extended one */ +/* return 0 on failure */ +/* ep is 0 or points to surrounding extended partition */ +static int +compute_start_sect(struct part_desc *p, struct part_desc *ep) { + unsigned long long base; + int inc = (DOS && B.sectors) ? B.sectors : 1; + long long delta; + + if (ep && p->start + p->size >= ep->start + 1) + delta = p->start - ep->start - inc; + else if (p->start == 0 && p->size > 0) + delta = -inc; + else + delta = 0; + + if (delta < 0) { + unsigned long long old_size = p->size; + p->start -= delta; + p->size += delta; + if (is_extended(p->p.sys_type) && boxes == ONESECTOR) + p->size = inc; + else if ((long long) old_size <= -delta) { + warnx(_("no room for partition descriptor")); + return 0; + } + } + base = (!ep ? 0 + : (is_extended(p->p.sys_type) ? + outer_extended_partition(ep) : ep)->start); + p->ep = ep; + if (p->p.sys_type == EMPTY_PARTITION && p->size == 0) { + p->p.start_sect = 0; + p->p.begin_chs = zero_chs; + p->p.end_chs = zero_chs; + } else { + p->p.start_sect = p->start - base; + p->p.begin_chs = ulong_to_chs(p->start, B); + p->p.end_chs = ulong_to_chs(p->start + p->size - 1, B); + } + p->p.nr_sects = p->size; + return 1; +} + +/* build the extended partition surrounding a given logical partition */ +static int +build_surrounding_extended(struct part_desc *p, struct part_desc *ep, + struct disk_desc *z) { + int inc = (DOS && B.sectors) ? B.sectors : 1; + int format = F_SECTOR; + struct part_desc *p0 = &(z->partitions[0]), *eep = ep->ep; + + if (boxes == NESTED) { + ep->start = first_free(ep - p0, 1, eep, format, p->start, z); + ep->size = max_length(ep - p0, 1, eep, format, ep->start, z); + if (ep->start > p->start || ep->start + ep->size < p->start + p->size) { + warnx(_("cannot build surrounding extended partition")); + return 0; + } + } else { + ep->start = p->start; + if (boxes == CHAINED) + ep->size = p->size; + else + ep->size = inc; + } + + ep->p.nr_sects = ep->size; + ep->p.bootable = 0; + ep->p.sys_type = EXTENDED_PARTITION; + if (!compute_start_sect(ep, eep) || !compute_start_sect(p, ep)) { + ep->p.sys_type = EMPTY_PARTITION; + ep->size = 0; + return 0; + } + + return 1; +} + +static int +read_line(int pno, struct part_desc *ep, char *dev, int interactive, + struct disk_desc *z) { + char line[1000]; + char *fields[11]; + int fno, pct = pno % 4; + struct part_desc p, *orig; + unsigned long long ff, ff1, ul, ml, ml1, def; + int format, lpno, is_extd; + + if (eof || eob) + return -1; + + lpno = index_to_linux(pno, z); + + if (interactive) { + if (pct == 0 && (show_extended || pno == 0)) + putchar('\n'); + warnx("%s:", partname(dev, lpno, 10)); + } + + /* read input line - skip blank lines when reading from a file */ + do { + fno = read_stdin(fields, line, ARRAY_SIZE(fields), ARRAY_SIZE(line)); + } while (fno == RD_CMD || (fno == 0 && !interactive)); + if (fno == RD_EOF) { + return -1; + } else if (fno > 10 && *(fields[10]) != 0) { + warnx(_("too many input fields")); + return 0; + } + + if (fno == 1 && !strcmp(fields[0], ".")) { + eob = 1; + return -1; + } + + /* use specified format, but round to cylinders if F_MEGABYTE specified */ + format = 0; + if (B.cylindersize && specified_format == F_MEGABYTE && !Linux) + format = F_CYLINDER; + + orig = (one_only ? &(oldp.partitions[pno]) : 0); + + p = zero_part_desc; + p.ep = ep; + + /* first read the type - we need to know whether it is extended */ + /* stop reading when input blank (defaults) and all is full */ + is_extd = 0; + if (fno == 0) { /* empty line */ + if (orig && is_extended(orig->p.sys_type)) + is_extd = 1; + ff = first_free(pno, is_extd, ep, format, 0, z); + ml = max_length(pno, is_extd, ep, format, ff, z); + if (ml == 0 && is_extd == 0) { + is_extd = 1; + ff = first_free(pno, is_extd, ep, format, 0, z); + ml = max_length(pno, is_extd, ep, format, ff, z); + } + if (ml == 0 && pno >= 4) { + /* no free blocks left - don't read any further */ + warnx(_("No room for more")); + return -1; + } + } + if (fno < 3 || !*(fields[2])) + ul = orig ? orig->p.sys_type : + (is_extd || (pno > 3 && pct == 1 && show_extended)) + ? EXTENDED_PARTITION : LINUX_NATIVE; + else if (!strcmp(fields[2], "L")) + ul = LINUX_NATIVE; + else if (!strcmp(fields[2], "S")) + ul = LINUX_SWAP; + else if (!strcmp(fields[2], "E")) + ul = EXTENDED_PARTITION; + else if (!strcmp(fields[2], "X")) + ul = LINUX_EXTENDED; + else if (get_ull(fields[2], &ul, LINUX_NATIVE, 16)) + return 0; + if (ul > 255) { + warnx(_("Illegal type")); + return 0; + } + p.p.sys_type = ul; + is_extd = is_extended(ul); + + /* find start */ + ff = first_free(pno, is_extd, ep, format, 0, z); + ff1 = ff * unitsize(format); + def = orig ? orig->start : (pno > 4 && pct > 1) ? 0 : ff1; + if (fno < 1 || !*(fields[0])) + p.start = def; + else { + if (get_ull(fields[0], &ul, def / unitsize(0), 0)) + return 0; + p.start = ul * unitsize(0); + p.start -= (p.start % unitsize(format)); + } + + /* find length */ + ml = max_length(pno, is_extd, ep, format, p.start / unitsize(format), z); + ml1 = ml * unitsize(format); + def = orig ? orig->size : (pno > 4 && pct > 1) ? 0 : ml1; + if (fno < 2 || !*(fields[1])) + p.size = def; + else if (!strcmp(fields[1], "+")) + p.size = ml1; + else { + if (get_ull(fields[1], &ul, def / unitsize(0), 0)) + return 0; + p.size = ul * unitsize(0) + unitsize(format) - 1; + p.size -= (p.size % unitsize(format)); + } + if (p.size > ml1) { + warnx(_("Warning: given size (%llu) exceeds max allowable size (%llu)"), + (p.size + unitsize(0) - 1) / unitsize(0), ml1 / unitsize(0)); + if (!force) + return 0; + } + if (p.size == 0 && pno >= 4 && (fno < 2 || !*(fields[1]))) { + warnx(_("Warning: empty partition")); + if (!force) + return 0; + } + p.p.nr_sects = p.size; + + if (p.size == 0 && !orig) { + if (fno < 1 || !*(fields[0])) + p.start = 0; + if (fno < 3 || !*(fields[2])) + p.p.sys_type = EMPTY_PARTITION; + } + + if (p.start < ff1 && p.size > 0) { + warnx(_("Warning: bad partition start (earliest %llu)"), + (ff1 + unitsize(0) - 1) / unitsize(0)); + if (!force) + return 0; + } + + if (fno < 4 || !*(fields[3])) + ul = (orig ? orig->p.bootable : 0); + else if (!strcmp(fields[3], "-")) + ul = 0; + else if (!strcmp(fields[3], "*") || !strcmp(fields[3], "+")) + ul = 0x80; + else { + warnx(_("unrecognized bootable flag - choose - or *")); + return 0; + } + p.p.bootable = ul; + + if (ep && ep->p.sys_type == EMPTY_PARTITION) { + if (!build_surrounding_extended(&p, ep, z)) + return 0; + } else if (!compute_start_sect(&p, ep)) + return 0; + + { + longchs aa = chs_to_longchs(p.p.begin_chs), bb; + + if (fno < 5) { + bb = aa; + } else if (fno < 7) { + warnx(_("partial c,h,s specification?")); + return 0; + } else if (get_ul(fields[4], &bb.c, aa.c, 0) || + get_ul(fields[5], &bb.h, aa.h, 0) || + get_ul(fields[6], &bb.s, aa.s, 0)) + return 0; + p.p.begin_chs = longchs_to_chs(bb, B); + } + { + longchs aa = chs_to_longchs(p.p.end_chs), bb; + + if (fno < 8) { + bb = aa; + } else if (fno < 10) { + warnx(_("partial c,h,s specification?")); + return 0; + } else if (get_ul(fields[7], &bb.c, aa.c, 0) || + get_ul(fields[8], &bb.h, aa.h, 0) || + get_ul(fields[9], &bb.s, aa.s, 0)) + return 0; + p.p.end_chs = longchs_to_chs(bb, B); + } + + if (pno > 3 && p.size && show_extended && p.p.sys_type != EMPTY_PARTITION + && (is_extended(p.p.sys_type) != (pct == 1))) { + warnx(_("Extended partition not where expected")); + if (!force) + return 0; + } + + z->partitions[pno] = p; + if (pno >= z->partno) + z->partno += 4; /* reqd for out_partition() */ + + if (interactive) + out_partition(dev, 0, &(z->partitions[pno]), z, B); + + return 1; +} + +/* ep either points to the extended partition to contain this one, + or to the empty partition that may become extended or is 0 */ +static int +read_partition(char *dev, int interactive, int pno, struct part_desc *ep, + struct disk_desc *z) { + struct part_desc *p = &(z->partitions[pno]); + int i; + + if (one_only) { + *p = oldp.partitions[pno]; + if (one_only_pno != pno) + goto ret; + } else if (!show_extended && pno > 4 && pno % 4) + goto ret; + + while (!(i = read_line(pno, ep, dev, interactive, z))) + if (!interactive) + errx(EXIT_FAILURE, _("bad input")); + if (i < 0) { + p->ep = ep; + return 0; + } + + ret: + p->ep = ep; + if (pno >= z->partno) + z->partno += 4; + return 1; +} + +static void +read_partition_chain(char *dev, int interactive, struct part_desc *ep, + struct disk_desc *z) { + int i; + size_t base; + + eob = 0; + while (1) { + base = z->partno; + if (base + 4 > ARRAY_SIZE(z->partitions)) { + warnx(_("too many partitions")); + break; + } + for (i = 0; i < 4; i++) + if (!read_partition(dev, interactive, base + i, ep, z)) + return; + for (i = 0; i < 4; i++) { + ep = &(z->partitions[base + i]); + if (is_extended(ep->p.sys_type) && ep->size) + break; + } + if (i == 4) { + /* nothing found - maybe an empty partition is going + to be extended */ + if (one_only || show_extended) + break; + ep = &(z->partitions[base + 1]); + if (ep->size || ep->p.sys_type != EMPTY_PARTITION) + break; + } + } +} + +static void +read_input(char *dev, int interactive, struct disk_desc *z) { + size_t i; + struct part_desc *partitions = &(z->partitions[0]), *ep; + + for (i = 0; i < ARRAY_SIZE(z->partitions); i++) + partitions[i] = zero_part_desc; + z->partno = 0; + + if (interactive) + warnx(_("Input in the following format; absent fields get a default value.\n" + " \n" + "Usually you only need to specify and (and perhaps ).")); + eof = 0; + + for (i = 0; i < 4; i++) + read_partition(dev, interactive, i, 0, z); + for (i = 0; i < 4; i++) { + ep = partitions + i; + if (is_extended(ep->p.sys_type) && ep->size) + read_partition_chain(dev, interactive, ep, z); + } + add_sector_and_offset(z); +} + +/* + * G. The command line + */ +static void usage(FILE * out) +{ + fputs(USAGE_HEADER, out); + fprintf(out, + _(" %s [options] [...]\n"), program_invocation_short_name); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -s, --show-size list size of a partition\n" + " -c, --id change or print partition Id\n" + " --change-id change Id\n" + " --print-id print Id\n"), out); + fputs(_(" -l, --list list partitions of each device\n" + " -d, --dump idem, but in a format suitable for later input\n" + " -i, --increment number cylinders etc. from 1 instead of from 0\n" + " -u, --unit units to be used; can be one of\n" + " S (sectors), C (cylinders), B (blocks), or M (MB)\n"), out); + fputs(_(" -1, --one-only reserved option that does nothing currently\n" + " -T, --list-types list the known partition types\n" + " -D, --DOS for DOS-compatibility: waste a little space\n" + " -E, --DOS-extended DOS extended partition compatibility\n" + " -R, --re-read make the kernel reread the partition table\n"), out); + fputs(_(" -N change only the partition with this \n" + " -n do not actually write to disk\n" + " -O save the sectors that will be overwritten to \n" + " -I restore sectors from \n"), out); + fputs(_(" -V, --verify check that the listed partitions are reasonable\n" + " -v, --version display version information and exit\n" + " -h, --help display this help text and exit\n"), out); + + fputs(_("\nDangerous options:\n"), out); + fputs(_(" -f, --force disable all consistency checking\n" + " --no-reread do not check whether the partition is in use\n" + " -q, --quiet suppress warning messages\n" + " -L, --Linux do not complain about things irrelevant for Linux\n"), out); + fputs(_(" -g, --show-geometry print the kernel's idea of the geometry\n" + " -G, --show-pt-geometry print geometry guessed from the partition table\n"), out); + fputs(_(" -A, --activate[=] activate bootable flag\n" + " -U, --unhide[=] set partition unhidden\n" + " -x, --show-extended also list extended partitions in the output,\n" + " or expect descriptors for them in the input\n"), out); + fputs(_(" --leave-last do not allocate the last cylinder\n" + " --IBM same as --leave-last\n"), out); + fputs(_(" --in-order partitions are in order\n" + " --not-in-order partitions are not in order\n" + " --inside-outer all logicals inside outermost extended\n" + " --not-inside-outer not all logicals inside outermost extended\n"), out); + fputs(_(" --nested every partition is disjoint from all others\n" + " --chained like nested, but extended partitions may lie outside\n" + " --onesector partitions are mutually disjoint\n"), out); + + fputs(_("\nOverride the detected geometry using:\n" + " -C, --cylinders set the number of cylinders to use\n" + " -H, --heads set the number of heads to use\n" + " -S, --sectors set the number of sectors to use\n"), out); + + fprintf(out, USAGE_MAN_TAIL("sfdisk(8)")); + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static void +activate_usage(void) { + char *p; + if (!strcmp(program_invocation_short_name, "activate")) + p = " "; + else + p = " --activate="; + fputs(USAGE_HEADER, stderr); + fputs(USAGE_SEPARATOR, stderr); + fprintf(stderr, _(" %s%sdevice list active partitions on device\n"), + program_invocation_short_name, p); + fprintf(stderr, _(" %s%sdevice n1 n2 ... activate partitions n1 ..., inactivate the rest\n"), + program_invocation_short_name, p); + fprintf(stderr, USAGE_MAN_TAIL("sfdisk(8)")); + exit(EXIT_FAILURE); +} + +static const char short_opts[] = "cdfghilnqsu:vx1A::C:DGH:I:LN:O:RS:TU::V"; + +#define PRINT_ID 0400 +#define CHANGE_ID 01000 + +enum { + OPT_NO_REREAD = CHAR_MAX + 1, + OPT_LEAVE_LAST, + OPT_IN_ORDER, + OPT_NOT_IN_ORDER, + OPT_INSIDE_OUTER, + OPT_NOT_INSIDE_OUTER, + OPT_NESTED, + OPT_CHAINED, + OPT_ONESECTOR +}; + +static const struct option long_opts[] = { + { "change-id", no_argument, NULL, 'c' + CHANGE_ID }, + { "print-id", no_argument, NULL, 'c' + PRINT_ID }, + { "id", no_argument, NULL, 'c' }, + { "dump", no_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "show-geometry", no_argument, NULL, 'g' }, + { "help", no_argument, NULL, 'h' }, + { "increment", no_argument, NULL, 'i' }, + { "list", no_argument, NULL, 'l' }, + { "quiet", no_argument, NULL, 'q' }, + { "show-size", no_argument, NULL, 's' }, + { "unit", required_argument, NULL, 'u' }, + { "version", no_argument, NULL, 'v' }, + { "show-extended", no_argument, NULL, 'x' }, + { "one-only", no_argument, NULL, '1' }, + { "cylinders", required_argument, NULL, 'C' }, + { "heads", required_argument, NULL, 'H' }, + { "sectors", required_argument, NULL, 'S' }, + { "show-pt-geometry", no_argument, NULL, 'G' }, + { "activate", optional_argument, NULL, 'A' }, + { "DOS", no_argument, NULL, 'D' }, + { "DOS-extended", no_argument, NULL, 'E' }, + { "Linux", no_argument, NULL, 'L' }, + { "re-read", no_argument, NULL, 'R' }, + { "list-types", no_argument, NULL, 'T' }, + { "unhide", optional_argument, NULL, 'U' }, + { "no-reread", no_argument, NULL, OPT_NO_REREAD }, + { "IBM", no_argument, NULL, OPT_LEAVE_LAST }, + { "leave-last", no_argument, NULL, OPT_LEAVE_LAST }, +/* dangerous flags - not all completely implemented */ + { "in-order", no_argument, NULL, OPT_IN_ORDER }, + { "not-in-order", no_argument, NULL, OPT_NOT_IN_ORDER }, + { "inside-outer", no_argument, NULL, OPT_INSIDE_OUTER }, + { "not-inside-outer", no_argument, NULL, OPT_NOT_INSIDE_OUTER }, + { "nested", no_argument, NULL, OPT_NESTED }, + { "chained", no_argument, NULL, OPT_CHAINED }, + { "onesector", no_argument, NULL, OPT_ONESECTOR }, + { NULL, 0, NULL, 0 } +}; + +static int is_ide_cdrom_or_tape(char *device) +{ + int fd, ret; + + if ((fd = open(device, O_RDONLY)) < 0) + return 0; + ret = blkdev_is_cdrom(fd); + + close(fd); + return ret; +} + +static char * +nextproc(FILE * procf) { + static char devname[256]; + char line[1024], ptname[128 + 1]; + int ma, mi; + unsigned long long sz; + + if (procf == NULL) + return NULL; + while (fgets(line, sizeof(line), procf) != NULL) { + if (sscanf(line, " %d %d %llu %128[^\n ]", &ma, &mi, &sz, ptname) != 4) + continue; + snprintf(devname, sizeof(devname), "/dev/%s", ptname); + if (!is_whole_disk(devname)) + continue; + return canonicalize_path(devname); + } + + return NULL; +} + +static void do_list(char *dev, int silent); +static void do_size(char *dev, int silent); +static void do_geom(char *dev, int silent); +static void do_pt_geom(char *dev, int silent); +static void do_fdisk(char *dev); +static void do_reread(char *dev); +static void do_change_id(char *dev, char *part, char *id); +static void do_unhide(char **av, int ac, char *arg); +static void do_activate(char **av, int ac, char *arg); + +unsigned long long total_size; + +int +main(int argc, char **argv) { + int c; + char *dev; + int opt_size = 0; + int opt_out_geom = 0; + int opt_out_pt_geom = 0; + int opt_reread = 0; + int activate = 0; + int do_id = 0; + int unhide = 0; + char *activatearg = 0; + char *unhidearg = 0; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + if (argc < 1) + errx(EXIT_FAILURE, _("no command?")); + if (!strcmp(program_invocation_short_name, "activate")) + activate = 1; /* equivalent to `sfdisk -A' */ + + while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + switch (c) { + case 'f': + force = 1; + break; /* does not imply quiet */ + case 'g': + opt_out_geom = 1; + break; + case 'G': + opt_out_pt_geom = 1; + break; + case 'i': + increment = 1; + break; + case 'c': + case 'c' + PRINT_ID: + case 'c' + CHANGE_ID: + do_id = c; + break; + case 'd': + dump = 1; /* fall through */ + case 'l': + opt_list = 1; + break; + case 'n': + no_write = 1; + break; + case 'q': + quiet = 1; + break; + case 's': + opt_size = 1; + break; + case 'u': + set_format(*optarg); + break; + case 'v': + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case 'h': + usage(stdout); + return EXIT_SUCCESS; + case 'x': + show_extended = 1; + break; + case 'A': + activatearg = optarg; + activate = 1; + break; + case 'C': + U.cylinders = strtoul_or_err(optarg, _("invalid cylinders argument")); + break; + case 'D': + DOS = 1; + break; + case 'E': + DOS_extended = 1; + break; + case 'H': + U.heads = strtoul_or_err(optarg, _("invalid heads argument")); + break; + case 'L': + Linux = 1; + break; + case 'N': + one_only = strtol_or_err(optarg, _("invalid number of partitions argument")); + break; + case 'I': + restore_sector_file = optarg; + break; + case 'O': + save_sector_file = optarg; + break; + case 'R': + opt_reread = 1; + break; + case 'S': + U.sectors = strtoul_or_err(optarg, _("invalid sectors argument")); + break; + case 'T': + list_types(); + return EXIT_SUCCESS; + case 'U': + unhidearg = optarg; + unhide = 1; + break; + case 'V': + verify = 1; + break; + default: + usage(stderr); + break; + + /* dangerous flags */ + case OPT_IN_ORDER: + partitions_in_order = 1; + break; + case OPT_NOT_IN_ORDER: + partitions_in_order = 0; + break; + case OPT_INSIDE_OUTER: + all_logicals_inside_outermost_extended = 1; + break; + case OPT_NOT_INSIDE_OUTER: + all_logicals_inside_outermost_extended = 0; + break; + case OPT_NESTED: + boxes = NESTED; + break; + case OPT_CHAINED: + boxes = CHAINED; + break; + case OPT_ONESECTOR: + boxes = ONESECTOR; + break; + + /* more flags */ + case OPT_NO_REREAD: + no_reread = 1; + break; + case OPT_LEAVE_LAST: + leave_last = 1; + break; + } + } + + if (optind == argc && + (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify)) { + FILE *procf; + + /* try all known devices */ + total_size = 0; + + procf = fopen(_PATH_PROC_PARTITIONS, "r"); + if (!procf) + fprintf(stderr, _("cannot open %s\n"), _PATH_PROC_PARTITIONS); + else { + while ((dev = nextproc(procf)) != NULL) { + if (!is_ide_cdrom_or_tape(dev)) { + if (opt_out_geom) + do_geom(dev, 1); + if (opt_out_pt_geom) + do_pt_geom(dev, 1); + if (opt_size) + do_size(dev, 1); + if (opt_list || verify) + do_list(dev, 1); + } + free(dev); + } + fclose(procf); + } + + if (opt_size) + printf(_("total: %llu blocks\n"), total_size); + + return exit_status; + } + + if (optind == argc) { + if (activate) + activate_usage(); + else + usage(stderr); + } + + if (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify) { + while (optind < argc) { + if (opt_out_geom) + do_geom(argv[optind], 0); + if (opt_out_pt_geom) + do_pt_geom(argv[optind], 0); + if (opt_size) + do_size(argv[optind], 0); + if (opt_list || verify) + do_list(argv[optind], 0); + optind++; + } + return exit_status; + } + + if (activate) { + do_activate(argv + optind, argc - optind, activatearg); + return exit_status; + } + if (unhide) { + do_unhide(argv + optind, argc - optind, unhidearg); + return exit_status; + } + if (do_id) { + if ((do_id & PRINT_ID) != 0 && optind != argc - 2) + errx(EXIT_FAILURE, _("usage: sfdisk --print-id device partition-number")); + else if ((do_id & CHANGE_ID) != 0 && optind != argc - 3) + errx(EXIT_FAILURE, _("usage: sfdisk --change-id device partition-number Id")); + else if (optind != argc - 3 && optind != argc - 2) + errx(EXIT_FAILURE, _("usage: sfdisk --id device partition-number [Id]")); + do_change_id(argv[optind], argv[optind + 1], + (optind == argc - 2) ? 0 : argv[optind + 2]); + return exit_status; + } + + if (optind != argc - 1) + errx(EXIT_FAILURE, _("can specify only one device (except with -l or -s)")); + dev = argv[optind]; + + if (opt_reread) + do_reread(dev); + else if (restore_sector_file) + restore_sectors(dev); + else + do_fdisk(dev); + + return exit_status; +} + +/* + * H. Listing the current situation + */ + +static int +my_open(char *dev, int rw, int silent) { + int fd, mode; + + mode = (rw ? O_RDWR : O_RDONLY); + fd = open(dev, mode); + if (fd < 0 && !silent) { + if (rw) + err(EXIT_FAILURE, _("cannot open %s read-write"), dev); + else + err(EXIT_FAILURE, _("cannot open %s for reading"), dev); + } + return fd; +} + +static void +do_list(char *dev, int silent) { + int fd; + struct disk_desc *z; + + fd = my_open(dev, 0, silent); + if (fd < 0) + return; + + z = &oldp; + + free_sectors(); + get_cylindersize(dev, fd, dump ? 1 : opt_list ? 0 : 1); + get_partitions(dev, fd, z); + + if (opt_list) + out_partitions(dev, z); + + if (verify) { + if (partitions_ok(fd, z)) + printf(_("%s: OK"), dev); + else + exit_status = 1; + } + + close(fd); +} + +static void +do_geom(char *dev, int silent) { + int fd; + struct geometry R; + + fd = my_open(dev, 0, silent); + if (fd < 0) + return; + + R = get_geometry(dev, fd, silent); + if (R.cylinders) + printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"), + dev, R.cylinders, R.heads, R.sectors); + + close(fd); +} + +static void +do_pt_geom(char *dev, int silent) { + int fd; + struct disk_desc *z; + struct geometry R; + + fd = my_open(dev, 0, silent); + if (fd < 0) + return; + + z = &oldp; + + free_sectors(); + get_cylindersize(dev, fd, 1); + get_partitions(dev, fd, z); + + R = B; + + if (z->partno != 0 && get_fdisk_geometry(z)) { + R.heads = F.heads; + R.sectors = F.sectors; + R.cylindersize = R.heads * R.sectors; + R.cylinders = (R.cylindersize == 0) ? 0 : R.total_size / R.cylindersize; + } + + if (R.cylinders) + printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"), + dev, R.cylinders, R.heads, R.sectors); + + close(fd); +} + +/* for compatibility with earlier fdisk: provide option -s */ +static void +do_size(char *dev, int silent) { + int fd; + unsigned long long size; + + fd = my_open(dev, 0, silent); + if (fd < 0) + return; + + if (blkdev_get_sectors(fd, &size) == -1) { + if (!silent) + err(EXIT_FAILURE, _("Cannot get size of %s"), dev); + goto done; + } + + size /= 2; /* convert sectors to blocks */ + + /* a CDROM drive without mounted CD yields MAXINT */ + if (silent && size == ((1 << 30) - 1)) + goto done; + + if (silent) + printf("%s: %9llu\n", dev, size); + else + printf("%llu\n", size); + + total_size += size; + +done: + close(fd); +} + +/* + * Activate: usually one wants to have a single primary partition + * to be active. OS/2 fdisk makes non-bootable logical partitions + * active - I don't know what that means to OS/2 Boot Manager. + * + * Call: activate /dev/hda 2 5 7 make these partitions active + * and the remaining ones inactive + * Or: sfdisk -A /dev/hda 2 5 7 + * + * If only a single partition must be active, one may also use the form + * sfdisk -A2 /dev/hda + * + * With "activate /dev/hda" or "sfdisk -A /dev/hda" the active partitions + * are listed but not changed. To get zero active partitions, use + * "activate /dev/hda none" or "sfdisk -A /dev/hda none". + * Use something like `echo ",,,*" | sfdisk -N2 /dev/hda' to only make + * /dev/hda2 active, without changing other partitions. + * + * A warning will be given if after the change not precisely one primary + * partition is active. + * + * The present syntax was chosen to be (somewhat) compatible with the + * activate from the LILO package. + */ +static void +set_active(struct disk_desc *z, char *pnam) { + int pno; + + pno = asc_to_index(pnam, z); + if (z->partitions[pno].ptype == DOS_TYPE) + z->partitions[pno].p.bootable = 0x80; +} + +static void +do_activate(char **av, int ac, char *arg) { + char *dev = av[0]; + int fd; + int rw, i, pno, lpno; + struct disk_desc *z; + + z = &oldp; + + rw = (!no_write && (arg || ac > 1)); + fd = my_open(dev, rw, 0); + + free_sectors(); + get_cylindersize(dev, fd, 1); + get_partitions(dev, fd, z); + + if (!arg && ac == 1) { + /* list active partitions */ + for (pno = 0; pno < z->partno; pno++) { + if (z->partitions[pno].p.bootable) { + lpno = index_to_linux(pno, z); + if (pno == linux_to_index(lpno, z)) + printf("%s\n", partname(dev, lpno, 0)); + else + printf("%s#%d\n", dev, pno); + if (z->partitions[pno].p.bootable != 0x80) + warnx(_("bad active byte: 0x%x instead of 0x80"), + z->partitions[pno].p.bootable); + } + } + } else { + /* clear `active byte' everywhere */ + for (pno = 0; pno < z->partno; pno++) + if (z->partitions[pno].ptype == DOS_TYPE) + z->partitions[pno].p.bootable = 0; + + /* then set where desired */ + if (ac == 1) + set_active(z, arg); + else + for (i = 1; i < ac; i++) + set_active(z, av[i]); + + /* then write to disk */ + if (write_partitions(dev, fd, z)) + warnx(_("Done")); + else + exit_status = 1; + } + i = 0; + for (pno = 0; pno < z->partno && pno < 4; pno++) + if (z->partitions[pno].p.bootable) + i++; + if (i != 1) + warnx(_("You have %d active primary partitions. This does not matter for LILO,\n" + "but the DOS MBR will only boot a disk with 1 active partition."), + i); + + if (close_fd(fd) != 0) { + warnx(_("write failed")); + exit_status = 1; + } +} + +static void +set_unhidden(struct disk_desc *z, char *pnam) { + int pno; + unsigned char id; + + pno = asc_to_index(pnam, z); + id = z->partitions[pno].p.sys_type; + if (id == 0x11 || id == 0x14 || id == 0x16 || id == 0x17 || + id == 0x17 || id == 0x1b || id == 0x1c || id == 0x1e) + id -= 0x10; + else + errx(EXIT_FAILURE, _("partition %s has id %x and is not hidden"), pnam, id); + z->partitions[pno].p.sys_type = id; +} + +/* + * maybe remove and make part of --change-id + */ +static void +do_unhide(char **av, int ac, char *arg) { + char *dev = av[0]; + int fd, rw, i; + struct disk_desc *z; + + z = &oldp; + + rw = !no_write; + fd = my_open(dev, rw, 0); + + free_sectors(); + get_cylindersize(dev, fd, 1); + get_partitions(dev, fd, z); + + /* unhide where desired */ + if (ac == 1) + set_unhidden(z, arg); + else + for (i = 1; i < ac; i++) + set_unhidden(z, av[i]); + + /* then write to disk */ + if (write_partitions(dev, fd, z)) + warn(_("Done")); + else + exit_status = 1; + + if (close_fd(fd) != 0) { + warn(_("write failed")); + exit_status = 1; + } +} + +static void +do_change_id(char *dev, char *pnam, char *id) { + int fd, rw, pno; + struct disk_desc *z; + unsigned long i; + + z = &oldp; + + rw = !no_write; + fd = my_open(dev, rw, 0); + + free_sectors(); + get_cylindersize(dev, fd, 1); + get_partitions(dev, fd, z); + + pno = asc_to_index(pnam, z); + if (id == 0) { + printf("%x\n", z->partitions[pno].p.sys_type); + goto done; + } + i = strtoul(id, NULL, 16); + if (i > 255) + errx(EXIT_FAILURE, _("Bad Id %lx"), i); + z->partitions[pno].p.sys_type = i; + + if (write_partitions(dev, fd, z)) + warnx(_("Done")); + else + exit_status = 1; + +done: + if (close_fd(fd) != 0) { + warnx(_("write failed")); + exit_status = 1; + } +} + +static void +do_reread(char *dev) { + int fd; + + fd = my_open(dev, 0, 0); + if (reread_ioctl(fd)) { + warnx(_("This disk is currently in use.")); + exit(EXIT_FAILURE); + } + + close(fd); +} + +/* + * I. Writing the new situation + */ + +static void +do_fdisk(char *dev) { + int fd; + char answer[32]; + struct stat statbuf; + int interactive = isatty(0); + struct disk_desc *z; + + if (stat(dev, &statbuf) < 0) + err(EXIT_FAILURE, _("Fatal error: cannot find %s"), dev); + if (!S_ISBLK(statbuf.st_mode)) { + warnx(_("Warning: %s is not a block device"), dev); + no_reread = 1; + } + fd = my_open(dev, !no_write, 0); + + if (!no_write && !no_reread) { + warnx(_("Checking that no-one is using this disk right now ...")); + if (reread_ioctl(fd)) { + warnx(_("\nThis disk is currently in use - repartitioning is probably a bad idea.\n" + "Umount all file systems, and swapoff all swap partitions on this disk.\n" + "Use the --no-reread flag to suppress this check.")); + if (!force) + errx(EXIT_FAILURE, _("Use the --force flag to overrule all checks.")); + } else + warnx(_("OK")); + } + + z = &oldp; + + free_sectors(); + get_cylindersize(dev, fd, 0); + get_partitions(dev, fd, z); + + printf(_("Old situation:\n")); + out_partitions(dev, z); + + if (one_only && (one_only_pno = linux_to_index(one_only, z)) < 0) + errx(EXIT_FAILURE, _("Partition %d does not exist, cannot change it"), one_only); + + z = &newp; + + while (1) { + + read_input(dev, interactive, z); + + printf(_("New situation:\n")); + out_partitions(dev, z); + + if (!partitions_ok(fd, z) && !force) { + if (!interactive) + errx(EXIT_FAILURE, _("I don't like these partitions - nothing changed.\n" + "(If you really want this, use the --force option.)")); + else + warnx(_("I don't like this - probably you should answer No")); + } + if (interactive) { + ask: + if (no_write) + /* TRANSLATORS: sfdisk uses rpmatch which means the answers y and n + * should be translated, but that is not the case with q answer. */ + printf(_("Are you satisfied with this? [ynq] ")); + else + printf(_("Do you want to write this to disk? [ynq] ")); + ignore_result( fgets(answer, sizeof(answer), stdin) ); + if (answer[0] == 'q' || answer[0] == 'Q') { + errx(EXIT_FAILURE, _("Quitting - nothing changed")); + } else if (rpmatch(answer) == 0) { + continue; + } else if (rpmatch(answer) == 1) { + break; + } else { + printf(_("Please answer one of y,n,q\n")); + goto ask; + } + } else + break; + } + + if (write_partitions(dev, fd, z)) + printf(_("Successfully wrote the new partition table\n\n")); + else + exit_status = 1; + + if (!reread_disk_partition(dev, fd)) { /* close fd on success */ + close(fd); + exit_status = 1; + } + warnx(_("If you created or changed a DOS partition, /dev/foo7, say, then use dd(1)\n" + "to zero the first 512 bytes: dd if=/dev/zero of=/dev/foo7 bs=512 count=1\n" + "(See fdisk(8).)")); + + sync(); /* superstition */ +} + + +/* + * return partition name - uses static storage unless buf is supplied + */ +static char *partname(char *dev, int pno, int lth) +{ + static char bufp[PATH_MAX]; + char *p; + int w, wp; + + w = strlen(dev); + p = ""; + + if (isdigit(dev[w-1])) + p = "p"; + + /* devfs kludge - note: fdisk partition names are not supposed + to equal kernel names, so there is no reason to do this */ + if (strcmp (dev + w - 4, "disc") == 0) { + w -= 4; + p = "part"; + } + + /* udev names partitions by appending -partN + e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 */ + if ((strncmp(dev, _PATH_DEV_BYID, strlen(_PATH_DEV_BYID)) == 0) || + strncmp(dev, _PATH_DEV_BYPATH, strlen(_PATH_DEV_BYPATH)) == 0) { + p = "-part"; + } + + wp = strlen(p); + + if (lth) { + snprintf(bufp, sizeof(bufp), "%*.*s%s%-2u", + lth-wp-2, w, dev, p, pno); + } else { + snprintf(bufp, sizeof(bufp), "%.*s%s%-2u", w, dev, p, pno); + } + return bufp; +} + diff --git a/fdisks/Makemodule.am b/fdisks/Makemodule.am deleted file mode 100644 index a59195b66..000000000 --- a/fdisks/Makemodule.am +++ /dev/null @@ -1,77 +0,0 @@ - -if !ARCH_M68K - -if BUILD_FDISK -sbin_PROGRAMS += fdisk -dist_man_MANS += fdisks/fdisk.8 -fdisk_SOURCES = \ - fdisks/fdisk.c \ - fdisks/fdisk.h \ - fdisks/fdisk-menu.c - -fdisk_LDADD = $(LDADD) libcommon.la libfdisk.la -fdisk_CFLAGS = $(AM_CFLAGS) -I$(ul_libfdisk_incdir) - -if BUILD_LIBBLKID -fdisk_CFLAGS += -I$(ul_libblkid_incdir) -fdisk_LDADD += libblkid.la -endif - -if BUILD_LIBUUID -fdisk_CFLAGS += -I$(ul_libuuid_incdir) -fdisk_LDADD += libuuid.la -endif - -if HAVE_STATIC_FDISK -sbin_PROGRAMS += fdisk.static -fdisk_static_SOURCES = $(fdisk_SOURCES) -fdisk_static_LDFLAGS = -all-static -fdisk_static_CFLAGS = $(fdisk_CFLAGS) -fdisk_static_LDADD = $(fdisk_LDADD) -endif - -endif # BUILD_FDISK - - -if !ARCH_SPARC - -sbin_PROGRAMS += sfdisk -dist_man_MANS += fdisks/sfdisk.8 -sfdisk_SOURCES = fdisks/sfdisk.c -sfdisk_LDADD = $(LDADD) libcommon.la - -if HAVE_STATIC_SFDISK -sbin_PROGRAMS += sfdisk.static -sfdisk_static_SOURCES = $(sfdisk_SOURCES) -sfdisk_static_LDFLAGS = -all-static -sfdisk_static_LDADD = $(sfdisk_LDADD) -endif - - -if BUILD_CFDISK -sbin_PROGRAMS += cfdisk -dist_man_MANS += fdisks/cfdisk.8 -cfdisk_SOURCES = fdisks/cfdisk.c -cfdisk_LDADD = $(LDADD) libcommon.la libfdisk.la -cfdisk_CFLAGS = $(AM_CFLAGS) -I$(ul_libfdisk_incdir) - -if BUILD_LIBUUID -cfdisk_CFLAGS += -I$(ul_libuuid_incdir) -cfdisk_LDADD += libuuid.la -endif - -if BUILD_LIBBLKID -cfdisk_CFLAGS += -I$(ul_libblkid_incdir) -cfdisk_LDADD += libblkid.la -endif - -if HAVE_SLANG -cfdisk_LDADD += -lslang -else -cfdisk_CFLAGS += $(NCURSES_CFLAGS) -cfdisk_LDADD += $(NCURSES_LIBS) -endif -endif # BUILD_CFDISK - -endif # !ARCH_SPARC -endif # !ARCH_M68K diff --git a/fdisks/cfdisk.8 b/fdisks/cfdisk.8 deleted file mode 100644 index 2fae5925f..000000000 --- a/fdisks/cfdisk.8 +++ /dev/null @@ -1,145 +0,0 @@ -.\" cfdisk.8 -- man page for cfdisk -.\" Copyright 1994 Kevin E. Martin (martin@cs.unc.edu) -.\" Copyright (C) 2014 Karel Zak -.\" -.\" Permission is granted to make and distribute verbatim copies of this -.\" manual provided the copyright notice and this permission notice are -.\" preserved on all copies. -.\" -.\" Permission is granted to copy and distribute modified versions of this -.\" manual under the conditions for verbatim copying, provided that the -.\" entire resulting derived work is distributed under the terms of a -.\" permission notice identical to this one. -.\" -.\" " for hilit mode -.TH CFDISK 8 "March 2014" "util-linux" "System Administration" -.SH NAME -cfdisk \- display or manipulate disk partition table -.SH SYNOPSIS -.B cfdisk -.RB [ options ] -.RI [ device ] -.SH DESCRIPTION -.B cfdisk -is a curses-based program for partitioning any block device. -The default device is /dev/sda. - -Note that -.B cfdisk -provides basic partitioning functionality by user friendly interface. If you -need advanced features then use -.BR fdisk (8). - -Since version 2.25 -.BR cfdisk (8) -supports MBR (DOS), GPT, SUN and SGI disk labels, but it does not provides any -functionality in regards to CHS (Cylinder-Head-Sector) addressing. CHS has -never been important for Linux and this addressing concept does not make any -sense for new devices. - -Since version 2.25 -.BR cfdisk (8) -also does not provide a 'print' command. This functionality is provided by -utils -.BR partx (8) -and -.BR lsblk (8) -in very comfortable and rich way. - -If you want to remove an old partition table from device than use -.BR wipefs (8). - -.SH OPTIONS -.IP "\fB\-h, \-\-help\fP" -Display help text and exit. -.IP "\fB\-L\fR, \fB\-\-color\fR[=\fIwhen\fR]" -Colorize output, enabled by default. The optional argument \fIwhen\fP can be -\fBauto\fR, \fBnever\fR or \fBalways\fR. If the \fIwhen\fR argument is omitted, -then it defaults to \fBauto\fR. -.IP "\fB-V, \-\-version" -Display version information and exit. - -.SH COMMANDS -.B cfdisk -commands can be entered by pressing the desired key (pressing -.I Enter -after the command is not necessary). Here is a list of the available -commands: -.TP -.B b -Toggle bootable flag of the current partition. This allows you to -select which primary partition is bootable on the drive. This command does not -have to available for all partition label types. -.TP -.B d -Delete the current partition. This will convert the current partition -into free space and merge it with any free space immediately -surrounding the current partition. A partition already marked as free -space or marked as unusable cannot be deleted. -.TP -.B h -Print the help screen. -.TP -.B n -Create a new partition from free space. -.B cfdisk -next prompts you for the size of the partition you want to create. -The default size is equal to the entire available free space at the current -position. - -The size may be followed by the multiplicative suffixes KiB=1024, -MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB" -is optional, e.g. "K" has the same meaning as "KiB") -.TP -.B q -Quit program. This will exit the program without writing any data to -disk. -.TP -.B t -Change the partition type. By default, new partitions are created as -.I Linux -partitions. -.TP -.B W -Write the partition table to disk (you must enter an uppercase W). Since -this might destroy data on the disk, you must either confirm or deny -the write by entering `yes' or `no'. If you enter `yes', -.B cfdisk -will write the partition table to disk and then tell the kernel to re-read the -partition table from the disk. - -The re-reading of the partition table does not work in some cases. In such a -case you need to inform the kernel about new partitions by -.BR partprobe (8), -.BR kpartx (8) -or reboot the system. -.TP -.IR "Up Arrow" , " Down Arrow" -Move the cursor to the previous or next partition. If there are more -partitions than can be displayed on a screen, you can display the next -(previous) set of partitions by moving down (up) at the last (first) -partition displayed on the screen. - -.PP -All of the commands can be entered with either upper or lower case -letters (except for -.BR W rite). -When in a sub-menu or at a prompt to enter a filename, you can hit the -.I ESC -key to return to the main command line. - -.SH "SEE ALSO" -.BR fdisk (8), -.BR sfdisk (8), -.BR parted (8), -.BR partprobe (8), -.BR partx(8) -.SH AUTHOR -Karel Zak -.PP -The current cfdisk implemntation is based on the original cfdisk -from Kevin E. Martin (martin@cs.unc.edu). - -.SH AVAILABILITY -The cfdisk command is part of the util-linux package and is available from -ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/fdisks/cfdisk.c b/fdisks/cfdisk.c deleted file mode 100644 index 0fd64bf61..000000000 --- a/fdisks/cfdisk.c +++ /dev/null @@ -1,1842 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SLANG_H -# include -#elif defined(HAVE_SLANG_SLANG_H) -# include -#endif - -#ifdef HAVE_SLCURSES_H -# include -#elif defined(HAVE_SLANG_SLCURSES_H) -# include -#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR) -# include -#elif defined(HAVE_NCURSES_H) -# include -#elif defined(HAVE_NCURSES_NCURSES_H) -# include -#endif - -#ifdef HAVE_WIDECHAR -# include -#endif - -#include "c.h" -#include "closestream.h" -#include "nls.h" -#include "strutils.h" -#include "xalloc.h" -#include "mbsalign.h" -#include "colors.h" - -#include "fdiskP.h" - -#define ARROW_CURSOR_STRING " >> " -#define ARROW_CURSOR_DUMMY " " -#define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1) - -#define MENU_PADDING 2 -#define TABLE_START_LINE 4 -#define MENU_START_LINE ((size_t) LINES - 5) -#define INFO_LINE ((size_t) LINES - 2) -#define HINT_LINE ((size_t) LINES - 1) - -#define CFDISK_ERR_ESC 5000 - -#ifndef KEY_ESC -# define KEY_ESC '\033' -#endif -#ifndef KEY_DELETE -# define KEY_DELETE '\177' -#endif - -/* colors */ -enum { - CFDISK_CL_NONE = 0, - CFDISK_CL_WARNING, - CFDISK_CL_FREESPACE, -}; -static const int color_pairs[][2] = { - /* color foreground, background */ - [CFDISK_CL_WARNING] = { COLOR_RED, -1 }, - [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 } -}; - -struct cfdisk; - -static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx); -static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx); -static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item); -static struct cfdisk_menu *menu_pop(struct cfdisk *cf); - -static int ui_refresh(struct cfdisk *cf); -static void ui_warnx(const char *fmt, ...); -static void ui_warn(const char *fmt, ...); -static void ui_info(const char *fmt, ...); -static void ui_draw_menu(struct cfdisk *cf); -static int ui_menu_move(struct cfdisk *cf, int key); - -static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res, - uintmax_t low, uintmax_t up); - -static int ui_enabled; - -/* menu item */ -struct cfdisk_menuitem { - int key; /* keyboard shortcut */ - const char *name; /* item name */ - const char *desc; /* item description (hint) */ - void *userdata; -}; - -/* menu */ -struct cfdisk_menu { - char *title; /* optional menu title */ - struct cfdisk_menuitem *items; /* array with menu items */ - char *ignore;/* string with keys to ignore */ - size_t width; /* maximal width of the menu item */ - size_t nitems; /* number of the active menu items */ - size_t page_sz;/* when menu longer than screen */ - size_t idx; /* the current menu item */ - struct cfdisk_menu *prev; - - /* @ignore keys generator */ - int (*ignore_cb) (struct cfdisk *, char *, size_t); - - unsigned int vertical : 1; /* enable vertical mode */ -}; - -/* main menu */ -static struct cfdisk_menuitem main_menuitems[] = { - { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") }, - { 'd', N_("Delete"), N_("Delete the current partition") }, - { 'n', N_("New"), N_("Create new partition from free space") }, - { 'q', N_("Quit"), N_("Quit program without writing partition table") }, - { 't', N_("Type"), N_("Change the partition type") }, - { 'h', N_("Help"), N_("Print help screen") }, - { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") }, - { 0, NULL, NULL } -}; - -/* top level control struct */ -struct cfdisk { - struct fdisk_context *cxt; /* libfdisk context */ - struct fdisk_table *table; /* partition table */ - struct cfdisk_menu *menu; /* the current menu */ - - int *cols; /* output columns */ - size_t ncols; /* number of columns */ - - char *linesbuf; /* table as string */ - size_t linesbufsz; /* size of the tb_buf */ - - char **lines; /* array with lines */ - size_t nlines; /* number of lines */ - size_t lines_idx; /* current line <0..N>, exclude header */ - size_t page_sz; -}; - -/* Initialize output columns -- we follow libcfdisk columns (usually specific - * to the label type. - */ -static int cols_init(struct cfdisk *cf) -{ - assert(cf); - - free(cf->cols); - cf->cols = NULL; - cf->ncols = 0; - - return fdisk_get_columns(cf->cxt, 0, &cf->cols, &cf->ncols); -} - -/* It would be possible to use fdisk_table_to_string(), but we want some - * extension to the output format, so let's do it without libfdisk - */ -static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb) -{ - const struct fdisk_column *col; - struct fdisk_partition *pa; - struct fdisk_label *lb; - struct fdisk_iter *itr = NULL; - struct tt *tt = NULL; - char *res = NULL; - size_t i; - int tree = 0; - struct tt_line *ln, *ln_cont = NULL; - - DBG(FRONTEND, dbgprint("table: convert to string")); - - assert(cf); - assert(cf->cxt); - assert(cf->cols); - assert(tb); - - lb = fdisk_context_get_label(cf->cxt, NULL); - assert(lb); - - itr = fdisk_new_iter(FDISK_ITER_FORWARD); - if (!itr) - goto done; - - /* get container (e.g. extended partition) */ - while (fdisk_table_next_partition(tb, itr, &pa) == 0) { - if (fdisk_partition_is_nested(pa)) { - DBG(FRONTEND, dbgprint("table: nested detected, using tree")); - tree = TT_FL_TREE; - break; - } - } - - tt = tt_new_table(TT_FL_FREEDATA | TT_FL_MAX | tree); - if (!tt) - goto done; - - /* headers */ - for (i = 0; i < cf->ncols; i++) { - col = fdisk_label_get_column(lb, cf->cols[i]); - if (col) { - int fl = col->tt_flags; - if (tree && col->id == FDISK_COL_DEVICE) - fl |= TT_FL_TREE; - tt_define_column(tt, col->name, col->width, fl); - } - } - - /* data */ - fdisk_reset_iter(itr, FDISK_ITER_FORWARD); - - while (fdisk_table_next_partition(tb, itr, &pa) == 0) { - struct tt_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL; - - ln = tt_add_line(tt, parent); - if (!ln) - goto done; - for (i = 0; i < cf->ncols; i++) { - char *cdata = NULL; - col = fdisk_label_get_column(lb, cf->cols[i]); - if (!col) - continue; - if (fdisk_partition_to_string(pa, cf->cxt, col->id, &cdata)) - continue; - tt_line_set_data(ln, i, cdata); - } - if (tree && fdisk_partition_is_container(pa)) - ln_cont = ln; - - tt_line_set_userdata(ln, (void *) pa); - fdisk_ref_partition(pa); - } - - if (tt_is_empty(tt)) - goto done; - - tt_set_termreduce(tt, ARROW_CURSOR_WIDTH); - tt_print_table_to_string(tt, &res); - - /* tt_* code might to reorder lines, let's reorder @tb according to the - * final output (it's no problem because partitions are addressed by - * parno stored within struct fdisk_partition) */ - - /* remove all */ - fdisk_reset_iter(itr, FDISK_ITER_FORWARD); - while (fdisk_table_next_partition(tb, itr, &pa) == 0) - fdisk_table_remove_partition(tb, pa); - - /* add all in the right order */ - i = 0; - while (tt_get_output_line(tt, i++, &ln) == 0) { - struct fdisk_partition *pa = tt_line_get_userdata(ln); - - fdisk_table_add_partition(tb, pa); - fdisk_unref_partition(pa); - } -done: - tt_free_table(tt); - fdisk_free_iter(itr); - - return res; -} - -/* - * Read data about partitions from libfdisk and prepare output lines. - */ -static int lines_refresh(struct cfdisk *cf) -{ - int rc; - char *p; - size_t i; - - assert(cf); - - DBG(FRONTEND, dbgprint("refreshing buffer")); - - free(cf->linesbuf); - free(cf->lines); - cf->linesbuf = NULL; - cf->linesbufsz = 0; - cf->lines = NULL; - cf->nlines = 0; - - fdisk_unref_table(cf->table); - cf->table = NULL; - - /* read partitions and free spaces into cf->table */ - rc = fdisk_get_partitions(cf->cxt, &cf->table); - if (!rc) - rc = fdisk_get_freespaces(cf->cxt, &cf->table); - if (rc) - return rc; - - cf->linesbuf = table_to_string(cf, cf->table); - if (!cf->linesbuf) - return -ENOMEM; - - cf->linesbufsz = strlen(cf->linesbuf); - cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */ - cf->page_sz = 0; - - if (MENU_START_LINE - TABLE_START_LINE < cf->nlines) - cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1; - - cf->lines = xcalloc(cf->nlines, sizeof(char *)); - - for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) { - cf->lines[i] = p; - p = strchr(p, '\n'); - if (p) { - *p = '\0'; - p++; - } - } - - return 0; -} - -static struct fdisk_partition *get_current_partition(struct cfdisk *cf) -{ - assert(cf); - assert(cf->table); - - return fdisk_table_get_partition(cf->table, cf->lines_idx); -} - -static int is_freespace(struct cfdisk *cf, size_t i) -{ - struct fdisk_partition *pa; - - assert(cf); - assert(cf->table); - - pa = fdisk_table_get_partition(cf->table, i); - return fdisk_partition_is_freespace(pa); -} - -/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's - * responseback to libfdisk - */ -static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf) -{ - struct cfdisk_menuitem *d, *cm; - int key; - size_t i = 0, nitems; - const char *name, *desc; - - assert(ask); - assert(cf); - - /* create cfdisk menu according to libfdisk ask-menu, note that the - * last cm[] item has to be empty -- so nitems + 1 */ - nitems = fdisk_ask_menu_get_nitems(ask); - cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem)); - - for (i = 0; i < nitems; i++) { - if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc)) - break; - cm[i].key = key; - cm[i].desc = desc; - cm[i].name = name; - } - - /* make the new menu active */ - menu_push(cf, cm); - ui_draw_menu(cf); - refresh(); - - /* wait for keys */ - do { - int key = getch(); - - if (ui_menu_move(cf, key) == 0) - continue; - - switch (key) { - case KEY_ENTER: - case '\n': - case '\r': - d = menu_get_menuitem(cf, cf->menu->idx); - if (d) - fdisk_ask_menu_set_result(ask, d->key); - menu_pop(cf); - free(cm); - return 0; - } - } while (1); - - menu_pop(cf); - free(cm); - return -1; -} - -/* libfdisk callback - */ -static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, - void *data __attribute__((__unused__))) -{ - int rc = 0; - - assert(cxt); - assert(ask); - - switch(fdisk_ask_get_type(ask)) { - case FDISK_ASKTYPE_INFO: - ui_info(fdisk_ask_print_get_mesg(ask)); - break; - case FDISK_ASKTYPE_WARNX: - ui_warnx(fdisk_ask_print_get_mesg(ask)); - break; - case FDISK_ASKTYPE_WARN: - ui_warn(fdisk_ask_print_get_mesg(ask)); - break; - case FDISK_ASKTYPE_MENU: - ask_menu(ask, (struct cfdisk *) data); - break; - default: - ui_warnx(_("internal error: unsupported dialog type %d"), - fdisk_ask_get_type(ask)); - return -EINVAL; - } - return rc; -} - -static int ui_end(void) -{ - if (!ui_enabled) - return -EINVAL; - -#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H) - SLsmg_gotorc(LINES - 1, 0); - SLsmg_refresh(); -#else - mvcur(0, COLS - 1, LINES-1, 0); -#endif - nl(); - endwin(); - printf("\n"); - ui_enabled = 0; - return 0; -} - -static void ui_vprint_center(int line, int attrs, const char *fmt, va_list ap) -{ - size_t width; - char *buf = NULL; - - move(line, 0); - clrtoeol(); - - xvasprintf(&buf, fmt, ap); - - width = mbs_safe_width(buf); - if (width > (size_t) COLS) { - char *p = strrchr(buf + COLS, ' '); - if (!p) - p = buf + COLS; - *p = '\0'; - if (line + 1 >= LINES) - line--; - attron(attrs); - mvaddstr(line, 0, buf); - mvaddstr(line + 1, 0, p+1); - attroff(attrs); - } else { - attron(attrs); - mvaddstr(line, (COLS - width) / 2, buf); - attroff(attrs); - } - free(buf); -} - -static void ui_center(int line, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - ui_vprint_center(line, 0, fmt, ap); - va_end(ap); -} - -static void ui_warnx(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - if (ui_enabled) - ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt, ap); - else - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -static void ui_warn(const char *fmt, ...) -{ - char *fmt_m; - va_list ap; - - xasprintf(&fmt_m, "%s: %m", fmt); - - va_start(ap, fmt); - if (ui_enabled) - ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt_m, ap); - else - vfprintf(stderr, fmt_m, ap); - va_end(ap); - free(fmt_m); -} - -static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...) - { - va_list ap; - ui_end(); - - va_start(ap, fmt); - fprintf(stderr, "%s: ", program_invocation_short_name); - vfprintf(stderr, fmt, ap); - va_end(ap); - - exit(rc); -} - -static void ui_info(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - if (ui_enabled) - ui_vprint_center(INFO_LINE, A_BOLD, fmt, ap); - else - vfprintf(stdout, fmt, ap); - va_end(ap); -} - -static void ui_clean_info(void) -{ - move(INFO_LINE, 0); - clrtoeol(); -} - -static void ui_hint(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - if (ui_enabled) - ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap); - else - vfprintf(stdout, fmt, ap); - va_end(ap); -} - -static void ui_clean_hint(void) -{ - move(HINT_LINE, 0); - clrtoeol(); -} - -static void die_on_signal(int dummy __attribute__((__unused__))) -{ - DBG(FRONTEND, dbgprint("die on signal.")); - ui_end(); - exit(EXIT_FAILURE); -} - -static void menu_update_ignore(struct cfdisk *cf) -{ - char ignore[128] = { 0 }; - int i = 0; - struct cfdisk_menu *m; - struct cfdisk_menuitem *d, *org; - size_t idx; - - assert(cf); - assert(cf->menu); - assert(cf->menu->ignore_cb); - - m = cf->menu; - org = menu_get_menuitem(cf, m->idx); - - DBG(FRONTEND, dbgprint("menu: update menu ignored keys")); - - i = m->ignore_cb(cf, ignore, sizeof(ignore)); - ignore[i] = '\0'; - - /* return if no change */ - if ( (!m->ignore && !*ignore) - || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) { - return; - } - - free(m->ignore); - m->ignore = xstrdup(ignore); - m->nitems = 0; - - for (d = m->items; d->name; d++) { - if (m->ignore && strchr(m->ignore, d->key)) - continue; - m->nitems++; - } - - /* refresh menu index to be at the same menuitem or go to the first */ - if (org && menu_get_menuitem_by_key(cf, org->key, &idx)) - m->idx = idx; - else - m->idx = 0; - - m->page_sz = m->nitems / (LINES - 4) ? LINES - 4 : 0; -} - -static struct cfdisk_menu *menu_push( - struct cfdisk *cf, - struct cfdisk_menuitem *items) -{ - struct cfdisk_menu *m = xcalloc(1, sizeof(*m)); - struct cfdisk_menuitem *d; - - assert(cf); - - DBG(FRONTEND, dbgprint("menu: new menu")); - - m->prev = cf->menu; - m->items = items; - - for (d = m->items; d->name; d++) { - const char *name = _(d->name); - size_t len = mbs_safe_width(name); - if (len > m->width) - m->width = len; - m->nitems++; - } - - cf->menu = m; - m->page_sz = m->nitems / (LINES - 4) ? LINES - 4 : 0; - return m; -} - -static struct cfdisk_menu *menu_pop(struct cfdisk *cf) -{ - struct cfdisk_menu *m = NULL; - - assert(cf); - - DBG(FRONTEND, dbgprint("menu: rem menu")); - - if (cf->menu) { - m = cf->menu->prev; - free(cf->menu->ignore); - free(cf->menu->title); - free(cf->menu); - } - cf->menu = m; - return cf->menu; -} - -static void menu_set_title(struct cfdisk_menu *m, const char *title) -{ - char *str = NULL; - - if (title) { - size_t len = mbs_safe_width(title); - if (len + 3 > m->width) - m->width = len + 3; - str = xstrdup(title); - } - m->title = str; -} - - -static int ui_init(struct cfdisk *cf __attribute__((__unused__))) -{ - struct sigaction sa; - - DBG(FRONTEND, dbgprint("ui: init")); - - /* setup SIGCHLD handler */ - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = die_on_signal; - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - ui_enabled = 1; - initscr(); - - if (colors_wanted() && has_colors()) { - size_t i; - - start_color(); - use_default_colors(); - - for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */ - init_pair(i, color_pairs[i][0], color_pairs[i][1]); - } - - cbreak(); - noecho(); - nonl(); - curs_set(0); - keypad(stdscr, TRUE); - - return 0; -} - -static size_t menuitem_get_line(struct cfdisk *cf, size_t idx) -{ - struct cfdisk_menu *m = cf->menu; - - if (m->vertical) { - if (!m->page_sz) /* small menu */ - return (LINES - (cf->menu->nitems + 1)) / 2 + idx; - return (idx % m->page_sz) + 1; - } else { - size_t len = m->width + 4 + MENU_PADDING; /* item width */ - size_t items = COLS / len; /* items per line */ - - return MENU_START_LINE + ((idx / items)); - } -} - -static int menuitem_get_column(struct cfdisk *cf, size_t idx) -{ - if (cf->menu->vertical) { - size_t nc = cf->menu->width + MENU_PADDING; - if ((size_t) COLS <= nc) - return 0; - return (COLS - nc) / 2; - } else { - size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */ - size_t items = COLS / len; /* items per line */ - size_t extra = items < cf->menu->nitems ? /* extra space on line */ - COLS % len : /* - multi-line menu */ - COLS - (cf->menu->nitems * len); /* - one line menu */ - - extra += MENU_PADDING; /* add padding after last item to extra */ - - if (idx < items) - return (idx * len) + (extra / 2); - return ((idx % items) * len) + (extra / 2); - } -} - -static int menuitem_on_page(struct cfdisk *cf, size_t idx) -{ - struct cfdisk_menu *m = cf->menu; - - if (m->page_sz == 0 || - m->idx / m->page_sz == idx / m->page_sz) - return 1; - return 0; -} - -static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx) -{ - struct cfdisk_menuitem *d; - size_t i; - - for (i = 0, d = cf->menu->items; d->name; d++) { - if (cf->menu->ignore && strchr(cf->menu->ignore, d->key)) - continue; - if (i++ == idx) - return d; - } - - return NULL; -} - -static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, - int key, size_t *idx) -{ - struct cfdisk_menuitem *d; - - for (*idx = 0, d = cf->menu->items; d->name; d++) { - if (cf->menu->ignore && strchr(cf->menu->ignore, d->key)) - continue; - if (key == d->key) - return d; - (*idx)++; - } - - return NULL; -} - -static void ui_draw_menuitem(struct cfdisk *cf, - struct cfdisk_menuitem *d, - size_t idx) -{ - char buf[80 * MB_CUR_MAX]; - const char *name; - size_t width = cf->menu->width + 2; /* 2 = blank around string */ - int ln, cl, vert = cf->menu->vertical; - - if (!menuitem_on_page(cf, idx)) - return; /* no visible item */ - ln = menuitem_get_line(cf, idx); - cl = menuitem_get_column(cf, idx); - - name = _(d->name); - mbsalign(name, buf, sizeof(buf), &width, - vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER, - 0); - - DBG(FRONTEND, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'", - cl, ln, buf)); - - if (vert) { - mvaddch(ln, cl - 1, ACS_VLINE); - mvaddch(ln, cl + cf->menu->width + 4, ACS_VLINE); - } - - if (cf->menu->idx == idx) { - standout(); - mvprintw(ln, cl, vert ? " %s " : "[%s]", buf); - standend(); - if (d->desc) - ui_hint(d->desc); - } else - mvprintw(ln, cl, vert ? " %s " : "[%s]", buf); -} - -static void ui_draw_menu(struct cfdisk *cf) -{ - struct cfdisk_menuitem *d; - struct cfdisk_menu *m; - size_t i = 0; - size_t ln = menuitem_get_line(cf, 0); - size_t nlines; - - assert(cf); - assert(cf->menu); - - DBG(FRONTEND, dbgprint("ui: menu: draw start")); - - m = cf->menu; - - if (m->vertical) - nlines = m->page_sz ? m->page_sz : m->nitems; - else - nlines = menuitem_get_line(cf, m->nitems); - - for (i = ln; i <= ln + nlines; i++) { - move(i, 0); - clrtoeol(); - } - - if (m->ignore_cb) - menu_update_ignore(cf); - i = 0; - while ((d = menu_get_menuitem(cf, i))) - ui_draw_menuitem(cf, d, i++); - - if (m->vertical) { - size_t cl = menuitem_get_column(cf, 0); - size_t curpg = m->page_sz ? m->idx / m->page_sz : 0; - - /* corners and horizontal lines */ - mvaddch(ln - 1, cl - 1, ACS_ULCORNER); - mvaddch(ln + nlines, cl - 1, ACS_LLCORNER); - - for (i = 0; i < m->width + 4; i++) { - mvaddch(ln - 1, cl + i, ACS_HLINE); - mvaddch(ln + nlines, cl + i, ACS_HLINE); - } - - mvaddch(ln - 1, cl + i, ACS_URCORNER); - mvaddch(ln + nlines, cl + i, ACS_LRCORNER); - - /* draw also lines around empty lines on last page */ - if (m->page_sz && - m->nitems / m->page_sz == m->idx / m->page_sz) { - for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) { - mvaddch(i, cl - 1, ACS_VLINE); - mvaddch(i, cl + cf->menu->width + 4, ACS_VLINE); - } - } - if (m->title) { - attron(A_BOLD); - mvprintw(ln - 1, cl, " %s ", m->title); - attroff(A_BOLD); - } - if (curpg != 0) - mvaddch(ln - 1, cl + m->width + 3, ACS_UARROW); - if (m->page_sz && curpg < m->nitems / m->page_sz) - mvaddch(ln + nlines, cl + m->width + 3, ACS_DARROW); - } - - DBG(FRONTEND, dbgprint("ui: menu: draw end.")); -} - -static void ui_menu_goto(struct cfdisk *cf, int where) -{ - struct cfdisk_menuitem *d; - size_t old; - - /* stop and begin/end for vertical menus */ - if (cf->menu->vertical) { - if (where < 0) - where = 0; - else if (where > (int) cf->menu->nitems - 1) - where = cf->menu->nitems - 1; - } else { - /* continue from begin/end */ - if (where < 0) - where = cf->menu->nitems - 1; - else if ((size_t) where > cf->menu->nitems - 1) - where = 0; - } - if ((size_t) where == cf->menu->idx) - return; - - ui_clean_info(); - - old = cf->menu->idx; - cf->menu->idx = where; - - if (!menuitem_on_page(cf, old)) { - ui_draw_menu(cf); - return; - } - - d = menu_get_menuitem(cf, old); - ui_draw_menuitem(cf, d, old); - - d = menu_get_menuitem(cf, where); - ui_draw_menuitem(cf, d, where); -} - -static int ui_menu_move(struct cfdisk *cf, int key) -{ - struct cfdisk_menu *m; - - assert(cf); - assert(cf->menu); - - m = cf->menu; - - DBG(FRONTEND, dbgprint("ui: menu move key >%c<.", key)); - - if (m->vertical) - { - switch (key) { - case KEY_DOWN: - case '\016': /* ^N */ - case 'j': /* Vi-like alternative */ - ui_menu_goto(cf, m->idx + 1); - return 0; - case KEY_UP: - case '\020': /* ^P */ - case 'k': /* Vi-like alternative */ - ui_menu_goto(cf, (int) m->idx - 1); - return 0; - case KEY_PPAGE: - if (m->page_sz) { - ui_menu_goto(cf, (int) m->idx - m->page_sz); - return 0; - } - case KEY_HOME: - ui_menu_goto(cf, 0); - return 0; - case KEY_NPAGE: - if (m->page_sz) { - ui_menu_goto(cf, m->idx + m->page_sz); - return 0; - } - case KEY_END: - ui_menu_goto(cf, m->nitems); - return 0; - } - } else { - switch (key) { - case KEY_RIGHT: - case '\t': - ui_menu_goto(cf, m->idx + 1); - return 0; - case KEY_LEFT: -#ifdef KEY_BTAB - case KEY_BTAB: -#endif - ui_menu_goto(cf, (int) m->idx - 1); - return 0; - } - } - - return 1; /* key irrelevant for menu move */ -} - -static int partition_on_page(struct cfdisk *cf, size_t i) -{ - if (cf->page_sz == 0 || - cf->lines_idx / cf->page_sz == i / cf->page_sz) - return 1; - return 0; -} - -static void ui_draw_partition(struct cfdisk *cf, size_t i) -{ - int ln = TABLE_START_LINE + 1 + i; /* skip table header */ - int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */ - int cur = cf->lines_idx == i; - size_t curpg = 0; - - if (cf->page_sz) { - if (!partition_on_page(cf, i)) - return; - ln = TABLE_START_LINE + (i % cf->page_sz) + 1; - curpg = cf->lines_idx / cf->page_sz; - } - - DBG(FRONTEND, dbgprint( - "ui: draw partition %zu [page_sz=%zu, " - "line=%d, idx=%zu]", - i, cf->page_sz, ln, cf->lines_idx)); - - if (cur) { - attron(A_REVERSE); - mvaddstr(ln, 0, ARROW_CURSOR_STRING); - mvaddstr(ln, cl, cf->lines[i + 1]); - attroff(A_REVERSE); - } else { - int at = 0; - - if (is_freespace(cf, i)) { - attron(COLOR_PAIR(CFDISK_CL_FREESPACE)); - at = 1; - } - mvaddstr(ln, 0, ARROW_CURSOR_DUMMY); - mvaddstr(ln, cl, cf->lines[i + 1]); - if (at) - attroff(COLOR_PAIR(CFDISK_CL_FREESPACE)); - } - - if ((size_t) ln == MENU_START_LINE - 1 && - cf->page_sz && curpg < cf->nlines / cf->page_sz) { - if (cur) - attron(A_REVERSE); - mvaddch(ln, COLS - 1, ACS_DARROW); - mvaddch(ln, 0, ACS_DARROW); - if (cur) - attroff(A_REVERSE); - } -} - -static int ui_draw_table(struct cfdisk *cf) -{ - int cl = ARROW_CURSOR_WIDTH; - size_t i, nparts = fdisk_table_get_nents(cf->table); - size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0; - - DBG(FRONTEND, dbgprint("ui: draw table")); - - for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) { - move(i, 0); - clrtoeol(); - } - - if ((size_t) cf->lines_idx > nparts - 1) - cf->lines_idx = nparts ? nparts - 1 : 0; - - /* print header */ - attron(A_BOLD); - mvaddstr(TABLE_START_LINE, cl, cf->lines[0]); - attroff(A_BOLD); - - /* print partitions */ - for (i = 0; i < nparts; i++) - ui_draw_partition(cf, i); - - if (curpg != 0) { - mvaddch(TABLE_START_LINE, COLS - 1, ACS_UARROW); - mvaddch(TABLE_START_LINE, 0, ACS_UARROW); - } - if (cf->page_sz && curpg < cf->nlines / cf->page_sz) { - mvaddch(MENU_START_LINE - 1, COLS - 1, ACS_DARROW); - mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW); - } - return 0; -} - -static int ui_table_goto(struct cfdisk *cf, int where) -{ - size_t old; - size_t nparts = fdisk_table_get_nents(cf->table); - - DBG(FRONTEND, dbgprint("ui: goto table %d", where)); - - if (where < 0) - where = 0; - else if ((size_t) where > nparts - 1) - where = nparts - 1; - - if ((size_t) where == cf->lines_idx) - return 0; - - old = cf->lines_idx; - cf->lines_idx = where; - - if (!partition_on_page(cf, old) ||!partition_on_page(cf, where)) - ui_draw_table(cf); - else { - ui_draw_partition(cf, old); /* cleanup old */ - ui_draw_partition(cf, where); /* draw new */ - } - ui_clean_info(); - ui_draw_menu(cf); - refresh(); - return 0; -} - -static int ui_refresh(struct cfdisk *cf) -{ - char *id = NULL; - uint64_t bytes = cf->cxt->total_sectors * cf->cxt->sector_size; - char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE - | SIZE_SUFFIX_3LETTER, bytes); - erase(); - - if (!ui_enabled) - return -EINVAL; - - /* header */ - attron(A_BOLD); - ui_center(0, _("Disk: %s"), cf->cxt->dev_path); - attroff(A_BOLD); - ui_center(1, _("Size: %s, %ju bytes, %ju sectors"), - strsz, bytes, (uintmax_t) cf->cxt->total_sectors); - if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id) - ui_center(2, _("Label: %s, identifier: %s"), - cf->cxt->label->name, id); - else - ui_center(2, _("Label: %s"), cf->cxt->label->name); - free(strsz); - - ui_draw_table(cf); - ui_draw_menu(cf); - refresh(); - return 0; -} - -static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt, - const char *hint, char *buf, size_t len) -{ - size_t cells = 0; - ssize_t i = 0, rc = -1; - wint_t c; - int ln = MENU_START_LINE, cl = 1; - - assert(cf); - assert(buf); - assert(len); - - move(ln, 0); - clrtoeol(); - - if (prompt) { - mvaddstr(ln, cl, prompt); - cl += mbs_safe_width(prompt); - } - - /* default value */ - if (*buf) { - i = strlen(buf); - cells = mbs_safe_width(buf); - mvaddstr(ln, cl, buf); - } - - if (hint) - ui_hint(hint); - else - ui_clean_hint(); - - move(ln, cl + cells); - curs_set(1); - refresh(); - - while (1) { -#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \ - defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR) - if (get_wch(&c) == ERR) { -#else - if ((c = getch()) == ERR) { -#endif - if (!isatty(STDIN_FILENO)) - exit(2); - else - goto done; - } - if (c == '\r' || c == '\n' || c == KEY_ENTER) - break; - - switch (c) { - case KEY_ESC: - rc = -CFDISK_ERR_ESC; - goto done; - case KEY_DELETE: - case '\b': - case KEY_BACKSPACE: - if (i > 0) { - cells--; - i = mbs_truncate(buf, &cells); - if (i < 0) - goto done; - mvaddch(ln, cl + cells, ' '); - move(ln, cl + cells); - } else - beep(); - break; - default: -#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR) - if (i + 1 < (ssize_t) len && iswprint(c)) { - wchar_t wc = (wchar_t) c; - char s[MB_CUR_MAX + 1]; - int sz = wctomb(s, wc); - - if (sz > 0 && sz + i < (ssize_t) len) { - s[sz] = '\0'; - mvaddnstr(ln, cl + cells, s, sz); - memcpy(buf + i, s, sz); - i += sz; - buf[i] = '\0'; - cells += wcwidth(wc); - } else - beep(); - } -#else - if (i + 1 < (ssize_t) len && isprint(c)) { - mvaddch(ln, cl + cells, c); - str[i++] = c; - str[i] = '\0'; - cells++; - } -#endif - else - beep(); - } - refresh(); - } - - rc = i; /* success */ -done: - move(ln, 0); - clrtoeol(); - curs_set(0); - refresh(); - - return rc; -} - -/* @res is default value as well as result in bytes */ -static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res, - uintmax_t low, uintmax_t up) -{ - char buf[128]; - uintmax_t user = 0; - ssize_t rc; - char *dflt = size_to_human_string(0, *res); - - DBG(FRONTEND, dbgprint("ui: get_size (default=%ju)", *res)); - - ui_clean_info(); - - do { - int pwr = 0, insec = 0; - - snprintf(buf, sizeof(buf), "%s", dflt); - rc = ui_get_string(cf, prompt, - _("May be followed by {M,B,G,T}iB " - "(the \"iB\" is optional) or S for sectors."), - buf, sizeof(buf)); - if (rc == 0) { - ui_warnx(_("Please, specify size.")); - continue; /* nothing specified */ - } else if (rc == -CFDISK_ERR_ESC) - break; /* cancel dialog */ - - if (strcmp(buf, dflt) == 0) - user = *res, rc = 0; /* no change, use default */ - else { - size_t len = strlen(buf); - if (buf[len - 1] == 'S') { - insec = 1; - buf[len - 1] = '\0'; - } - rc = parse_size(buf, &user, &pwr); /* parse */ - } - - if (rc == 0) { - DBG(FRONTEND, dbgprint("ui: get_size user=%ju, power=%d, sectors=%s", - user, pwr, insec ? "yes" : "no")); - if (insec) - user *= cf->cxt->sector_size; - if (user < low) { - ui_warnx(_("Minimal size is %ju"), low); - rc = -ERANGE; - } - if (user > up && pwr && user < up + (1ULL << pwr * 10)) - /* ignore when the user specified size overflow - * with in range specified by suffix (e.g. MiB) */ - user = up; - - if (user > up) { - ui_warnx(_("Maximal size is %ju bytes."), up); - rc = -ERANGE; - } - } else - ui_warnx(_("Failed to parse size.")); - } while (rc != 0); - - if (rc == 0) - *res = user; - free(dflt); - - DBG(FRONTEND, dbgprint("ui: get_size (result=%ju, rc=%zd)", *res, rc)); - return rc; -} - -static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf, - struct fdisk_parttype *cur) -{ - struct cfdisk_menuitem *d, *cm; - size_t i = 0, nitems, idx = 0; - struct fdisk_parttype *t = NULL; - int has_typestr = 0; - - DBG(FRONTEND, dbgprint("ui: asking for parttype.")); - - /* create cfdisk menu according to label types, note that the - * last cm[] item has to be empty -- so nitems + 1 */ - nitems = cf->cxt->label->nparttypes; - if (!nitems) - return NULL; - cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem)); - if (!cm) - return NULL; - - has_typestr = cf->cxt->label->parttypes[0].typestr && - *cf->cxt->label->parttypes[0].typestr; - - for (i = 0; i < nitems; i++) { - struct fdisk_parttype *x = &cf->cxt->label->parttypes[i]; - char *name; - - if (!x || !x->name) - continue; - cm[i].userdata = x; - if (!has_typestr) - xasprintf(&name, "%2x %s", x->type, x->name); - else { - name = (char *) x->name; - cm[i].desc = x->typestr; - } - cm[i].name = name; - if (x == cur) - idx = i; - } - - /* make the new menu active */ - menu_push(cf, cm); - cf->menu->vertical = 1; - cf->menu->idx = idx; - menu_set_title(cf->menu, _("Select partition type")); - ui_draw_menu(cf); - refresh(); - - do { - int key = getch(); - - if (ui_menu_move(cf, key) == 0) - continue; - - switch (key) { - case KEY_ENTER: - case '\n': - case '\r': - d = menu_get_menuitem(cf, cf->menu->idx); - if (d) - t = (struct fdisk_parttype *) d->userdata; - goto done; - case 'q': - case 'Q': - goto done; - } - } while (1); - -done: - menu_pop(cf); - if (!has_typestr) { - for (i = 0; i < nitems; i++) - free((char *) cm[i].name); - } - free(cm); - DBG(FRONTEND, dbgprint("ui: get parrtype done [type=%s] ", t ? t->name : NULL)); - return t; -} - -/* prints menu with libfdisk labels and waits for users response */ -static int ui_create_label(struct cfdisk *cf) -{ - struct cfdisk_menuitem *d, *cm; - int rc = 1; - size_t i = 0, nitems; - struct fdisk_label *lb = NULL; - - assert(cf); - - DBG(FRONTEND, dbgprint("ui: asking for new disklabe.")); - - /* create cfdisk menu according to libfdisk labels, note that the - * last cm[] item has to be empty -- so nitems + 1 */ - nitems = fdisk_context_get_nlabels(cf->cxt); - cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem)); - - for (i = 0; i < nitems; i++) { - if (fdisk_context_next_label(cf->cxt, &lb)) - break; - cm[i].name = lb->name; - } - - erase(); - ui_center(LINES - 4, - _("Device does not contain a recognized partition table.")); - ui_center(LINES - 3, - _("Please, select a type to create a new disk label.")); - - /* make the new menu active */ - menu_push(cf, cm); - cf->menu->vertical = 1; - menu_set_title(cf->menu, _("Select label type")); - ui_draw_menu(cf); - refresh(); - - do { - int key = getch(); - if (ui_menu_move(cf, key) == 0) - continue; - switch (key) { - case KEY_ENTER: - case '\n': - case '\r': - d = menu_get_menuitem(cf, cf->menu->idx); - if (d) - rc = fdisk_create_disklabel(cf->cxt, d->name); - goto done; - case 'q': - case 'Q': - goto done; - } - } while (1); - -done: - menu_pop(cf); - free(cm); - DBG(FRONTEND, dbgprint("ui: create label done [rc=%d] ", rc)); - return rc; -} - -static int ui_help(void) -{ - size_t i; - static const char *help[] = { - N_("Help Screen for cfdisk"), - "", - N_("This is cfdisk, a curses based disk partitioning program, which"), - N_("allows you to create, delete and modify partitions on your hard"), - N_("disk drive."), - "", - N_("Copyright (C) 2014 Karel Zak "), - N_("Based on the original cfdisk from Kevin E. Martin & aeb."), - "", - N_("Command Meaning"), - N_("------- -------"), - N_(" b Toggle bootable flag of the current partition"), - N_(" d Delete the current partition"), - N_(" h Print this screen"), - N_(" n Create new partition from free space"), - N_(" q Quit program without writing partition table"), - N_(" t Change the partition type"), - N_(" W Write partition table to disk (must enter upper case W)"), - N_(" Since this might destroy data on the disk, you must"), - N_(" either confirm or deny the write by entering `yes' or"), - N_(" `no'"), - N_("Up Arrow Move cursor to the previous partition"), - N_("Down Arrow Move cursor to the next partition"), - N_("Left Arrow Move cursor to the previous menu item"), - N_("Right Arrow Move cursor to the next menu item"), - - "", - N_("Note: All of the commands can be entered with either upper or lower"), - N_("case letters (except for Writes)."), - "", - N_("Use lsblk(8) or partx(8) to see more details about the device.") - }; - - erase(); - for (i = 0; i < ARRAY_SIZE(help); i++) - mvaddstr(i, 1, _(help[i])); - - ui_info(_("Press a key to continue.")); - getch(); - return 0; -} - -/* TODO: use @sz, now 128bytes */ -static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore, - size_t sz __attribute__((__unused__))) -{ - struct fdisk_partition *pa = get_current_partition(cf); - size_t i = 0; - - if (!pa) - return 0; - if (fdisk_partition_is_freespace(pa)) { - ignore[i++] = 'd'; /* delete */ - ignore[i++] = 't'; /* set type */ - ignore[i++] = 'b'; /* set bootable */ - } else { - ignore[i++] = 'n'; - if (!fdisk_is_disklabel(cf->cxt, DOS) && - !fdisk_is_disklabel(cf->cxt, SGI)) - ignore[i++] = 'b'; - } - return i; -} - - -/* returns: error: < 0, success: 0, quit: 1 */ -static int main_menu_action(struct cfdisk *cf, int key) -{ - size_t n; - int ref = 0, rc; - const char *info = NULL, *warn = NULL; - struct fdisk_partition *pa; - - assert(cf); - assert(cf->cxt); - assert(cf->menu); - - if (key == 0) { - struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx); - if (!d) - return 0; - key = d->key; - - } else if (key != 'w' && key != 'W') - key = tolower(key); /* case insensitive except 'W'rite */ - - DBG(FRONTEND, dbgprint("ui: main menu action: key=%c", key)); - - if (cf->menu->ignore && strchr(cf->menu->ignore, key)) { - DBG(FRONTEND, dbgprint(" ignore '%c'", key)); - return 0; - } - - pa = get_current_partition(cf); - n = fdisk_partition_get_partno(pa); - - DBG(FRONTEND, dbgprint("menu action on %p", pa)); - ui_clean_hint(); - ui_clean_info(); - - switch (key) { - case 'b': /* Bootable flag */ - { - int fl = fdisk_is_disklabel(cf->cxt, DOS) ? DOS_FLAG_ACTIVE : - fdisk_is_disklabel(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0; - - if (fl && fdisk_partition_toggle_flag(cf->cxt, n, fl)) - warn = _("Could not toggle the flag."); - else if (fl) - ref = 1; - break; - } - case KEY_DC: - case 'd': /* Delete */ - if (fdisk_delete_partition(cf->cxt, n) != 0) - warn = _("Could not delete partition %zu."); - else - info = _("Partition %zu has been deleted."); - ref = 1; - break; - case 'h': /* help */ - ui_help(); - ref = 1; - break; - case 'n': /* New */ - { - uint64_t start, size, dflt_size; - struct fdisk_partition *npa; /* the new partition */ - - if (!pa || !fdisk_partition_is_freespace(pa)) - return -EINVAL; - npa = fdisk_new_partition(); - if (!npa) - return -ENOMEM; - /* free space range */ - start = fdisk_partition_get_start(pa); - size = dflt_size = fdisk_partition_get_size(pa) * cf->cxt->sector_size; - - if (ui_get_size(cf, _("Partition size: "), &size, 1, size) - == -CFDISK_ERR_ESC) - break; - - if (dflt_size == size) /* default is to fillin all free space */ - fdisk_partition_end_follow_default(npa, 1); - else /* set relative size of the partition */ - fdisk_partition_set_size(npa, size / cf->cxt->sector_size); - - fdisk_partition_set_start(npa, start); - fdisk_partition_partno_follow_default(npa, 1); - /* add to disk label -- libfdisk will ask for missing details */ - rc = fdisk_add_partition(cf->cxt, npa); - fdisk_unref_partition(npa); - if (rc == 0) - ref = 1; - break; - } - case 'q': /* Quit */ - return 1; - case 't': /* Type */ - { - struct fdisk_parttype *t; - - if (!pa || fdisk_partition_is_freespace(pa)) - return -EINVAL; - t = (struct fdisk_parttype *) fdisk_partition_get_type(pa); - t = ui_get_parttype(cf, t); - ref = 1; - - if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0) - info = _("Changed type of the partition %zu."); - else - info = _("Type of the partition %zu is unchanged."); - break; - } - case 'W': /* Write */ - { - char buf[64] = { 0 }; - int rc = ui_get_string(cf, - _("Are you sure you want to write the partition " - "table to disk? "), - _("Type \"yes\" or \"no\" or press ESC to left dialog."), - buf, sizeof(buf)); - - ref = 1; - if (rc <= 0 || strcasecmp(buf, "yes") != 0 - || strcasecmp(buf, _("yes")) != 0) { - info = _("Did not write partition table to disk"); - break; - } - rc = fdisk_write_disklabel(cf->cxt); - if (rc) - warn = _("Failed to write disklabel"); - else { - fdisk_reread_partition_table(cf->cxt); - info = _("The partition table has been altered."); - } - break; - } - default: - break; - } - - if (ref) { - lines_refresh(cf); - ui_refresh(cf); - } - - ui_clean_hint(); - if (warn) - ui_warnx(warn, n + 1); - else if (info) - ui_info(info, n + 1); - - return 0; -} - -static int ui_run(struct cfdisk *cf) -{ - int rc = 0; - - DBG(FRONTEND, dbgprint("ui: start COLS=%d, LINES=%d", COLS, LINES)); - - if (!fdisk_dev_has_disklabel(cf->cxt)) { - rc = ui_create_label(cf); - if (rc < 0) - ui_errx(EXIT_FAILURE, - _("failed to create a new disklabel")); - if (rc) - return rc; - } - - cols_init(cf); - rc = lines_refresh(cf); - if (rc) - ui_errx(EXIT_FAILURE, _("failed to read partitions")); - - menu_push(cf, main_menuitems); - cf->menu->ignore_cb = main_menu_ignore_keys; - - rc = ui_refresh(cf); - if (rc) - return rc; - - do { - int rc = 0, key = getch(); - - if (ui_menu_move(cf, key) == 0) - continue; - - DBG(FRONTEND, dbgprint("ui: main action key >%c<.", key)); - - switch (key) { - case KEY_DOWN: - case '\016': /* ^N */ - case 'j': /* Vi-like alternative */ - ui_table_goto(cf, cf->lines_idx + 1); - break; - case KEY_UP: - case '\020': /* ^P */ - case 'k': /* Vi-like alternative */ - ui_table_goto(cf, (int) cf->lines_idx - 1); - break; - case KEY_PPAGE: - if (cf->page_sz) { - ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz); - break; - } - case KEY_HOME: - ui_table_goto(cf, 0); - break; - case KEY_NPAGE: - if (cf->page_sz) { - ui_table_goto(cf, cf->lines_idx + cf->page_sz); - break; - } - case KEY_END: - ui_table_goto(cf, (int) cf->nlines - 1); - break; - case KEY_ENTER: - case '\n': - case '\r': - rc = main_menu_action(cf, 0); - break; - default: - rc = main_menu_action(cf, key); - if (rc < 0) - beep(); - break; - } - - if (rc == 1) - break; /* quit */ - } while (1); - - menu_pop(cf); - - DBG(FRONTEND, dbgprint("ui: end")); - - return 0; -} - -static void __attribute__ ((__noreturn__)) usage(FILE *out) -{ - fputs(USAGE_HEADER, out); - - fprintf(out, - _(" %1$s [options] \n"), program_invocation_short_name); - - fputs(USAGE_OPTIONS, out); - fputs(_(" -L --color[=] colorize output (auto, always or never)\n"), out); - - fputs(USAGE_SEPARATOR, out); - fputs(USAGE_HELP, out); - fputs(USAGE_VERSION, out); - - fprintf(out, USAGE_MAN_TAIL("cfdisk(8)")); - exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); -} - -int main(int argc, char *argv[]) -{ - int rc, c, colormode = UL_COLORMODE_AUTO; - struct cfdisk _cf = { .lines_idx = 0 }, - *cf = &_cf; - - static const struct option longopts[] = { - { "color", optional_argument, NULL, 'L' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { NULL, 0, 0, 0 }, - }; - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - atexit(close_stdout); - - while((c = getopt_long(argc, argv, "L::hV", longopts, NULL)) != -1) { - switch(c) { - case 'h': - usage(stdout); - break; - case 'L': - colormode = UL_COLORMODE_AUTO; - if (optarg) - colormode = colormode_or_err(optarg, - _("unsupported color mode")); - break; - case 'V': - printf(_("%s from %s\n"), program_invocation_short_name, - PACKAGE_STRING); - return EXIT_SUCCESS; - } - } - - colors_init(colormode); - - fdisk_init_debug(0); - cf->cxt = fdisk_new_context(); - if (!cf->cxt) - err(EXIT_FAILURE, _("failed to allocate libfdisk context")); - - fdisk_context_set_ask(cf->cxt, ask_callback, (void *) cf); - - if (optind == argc) - usage(stderr); - - if (fdisk_context_assign_device(cf->cxt, argv[optind], 0) != 0) - err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); - - /* Don't use err(), warn() from this point */ - ui_init(cf); - ui_run(cf); - ui_end(); - - free(cf->lines); - free(cf->linesbuf); - fdisk_unref_table(cf->table); - - rc = fdisk_context_deassign_device(cf->cxt); - fdisk_free_context(cf->cxt); - DBG(FRONTEND, dbgprint("bye! [rc=%d]", rc)); - return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/fdisks/fdisk-menu.c b/fdisks/fdisk-menu.c deleted file mode 100644 index d7b852deb..000000000 --- a/fdisks/fdisk-menu.c +++ /dev/null @@ -1,880 +0,0 @@ - -#include -#include -#include -#include -#include - -#include "c.h" -#include "fdisk.h" -#include "pt-sun.h" -#include "pt-mbr.h" - -struct menu_entry { - const char key; /* command key */ - const char *title; /* help string */ - unsigned int normal : 1, /* normal mode */ - expert : 1, /* expert mode */ - hidden : 1; /* be sensitive for this key, - but don't print it in help */ - - enum fdisk_labeltype label; /* only for this label */ - enum fdisk_labeltype exclude; /* all labels except this */ - enum fdisk_labeltype parent; /* for nested PT */ -}; - -#define IS_MENU_SEP(e) ((e)->key == '-') -#define IS_MENU_HID(e) ((e)->hidden) - -struct menu { - enum fdisk_labeltype label; /* only for this label */ - enum fdisk_labeltype exclude; /* all labels except this */ - - unsigned int nonested : 1; /* don't make this menu active in nested PT */ - - int (*callback)(struct fdisk_context **, - const struct menu *, - const struct menu_entry *); - - struct menu_entry entries[]; /* NULL terminated array */ -}; - -struct menu_context { - size_t menu_idx; /* the current menu */ - size_t entry_idx; /* index with in the current menu */ -}; - -#define MENU_CXT_EMPTY { 0, 0 } -#define DECLARE_MENU_CB(x) \ - static int x(struct fdisk_context **, \ - const struct menu *, \ - const struct menu_entry *) - -DECLARE_MENU_CB(gpt_menu_cb); -DECLARE_MENU_CB(sun_menu_cb); -DECLARE_MENU_CB(sgi_menu_cb); -DECLARE_MENU_CB(geo_menu_cb); -DECLARE_MENU_CB(dos_menu_cb); -DECLARE_MENU_CB(bsd_menu_cb); -DECLARE_MENU_CB(createlabel_menu_cb); -DECLARE_MENU_CB(generic_menu_cb); - -/* - * Menu entry macros: - * MENU_X* expert mode only - * MENU_B* both -- expert + normal mode - * - * *_E exclude this label - * *_H hidden - * *_L only for this label - */ - -/* separator */ -#define MENU_SEP(t) { .title = t, .key = '-', .normal = 1 } -#define MENU_XSEP(t) { .title = t, .key = '-', .expert = 1 } -#define MENU_BSEP(t) { .title = t, .key = '-', .expert = 1, .normal = 1 } - -/* entry */ -#define MENU_ENT(k, t) { .title = t, .key = k, .normal = 1 } -#define MENU_ENT_E(k, t, l) { .title = t, .key = k, .normal = 1, .exclude = l } -#define MENU_ENT_L(k, t, l) { .title = t, .key = k, .normal = 1, .label = l } - -#define MENU_XENT(k, t) { .title = t, .key = k, .expert = 1 } -#define MENU_XENT_H(k, t) { .title = t, .key = k, .expert = 1, .hidden = 1 } - -#define MENU_BENT(k, t) { .title = t, .key = k, .expert = 1, .normal = 1 } -#define MENU_BENT_E(k, t, l) { .title = t, .key = k, .expert = 1, .normal = 1, .exclude = l } - -#define MENU_ENT_NEST(k, t, l, p) { .title = t, .key = k, .normal = 1, .label = l, .parent = p } -#define MENU_XENT_NEST(k, t, l, p) { .title = t, .key = k, .expert = 1, .label = l, .parent = p } - -/* Generic menu */ -struct menu menu_generic = { - .callback = generic_menu_cb, - .entries = { - MENU_BSEP(N_("Generic")), - MENU_ENT ('d', N_("delete a partition")), - MENU_ENT ('l', N_("list known partition types")), - MENU_ENT ('n', N_("add a new partition")), - MENU_BENT ('p', N_("print the partition table")), - MENU_ENT ('t', N_("change a partition type")), - MENU_BENT_E('v', N_("verify the partition table"), FDISK_DISKLABEL_BSD), - - MENU_XENT('d', N_("print the raw data of the first sector from the device")), - MENU_XENT('D', N_("print the raw data of the disklabel from the device")), - - MENU_SEP(N_("Misc")), - MENU_BENT ('m', N_("print this menu")), - MENU_ENT_E('u', N_("change display/entry units"), FDISK_DISKLABEL_GPT), - MENU_ENT_E('x', N_("extra functionality (experts only)"), FDISK_DISKLABEL_BSD), - - MENU_BSEP(N_("Save & Exit")), - MENU_ENT_E('w', N_("write table to disk and exit"), FDISK_DISKLABEL_BSD), - MENU_ENT_L('w', N_("write table to disk"), FDISK_DISKLABEL_BSD), - MENU_BENT ('q', N_("quit without saving changes")), - MENU_XENT ('r', N_("return to main menu")), - - MENU_ENT_NEST('r', N_("return from BSD to DOS"), FDISK_DISKLABEL_BSD, FDISK_DISKLABEL_DOS), - - { 0, NULL } - } -}; - -struct menu menu_createlabel = { - .callback = createlabel_menu_cb, - .exclude = FDISK_DISKLABEL_BSD, - .nonested = 1, - .entries = { - MENU_SEP(N_("Create a new label")), - MENU_ENT('g', N_("create a new empty GPT partition table")), - MENU_ENT('G', N_("create a new empty SGI (IRIX) partition table")), - MENU_ENT('o', N_("create a new empty DOS partition table")), - MENU_ENT('s', N_("create a new empty Sun partition table")), - - /* backward compatibility -- be sensitive to 'g', but don't - * print it in the expert menu */ - MENU_XENT_H('g', N_("create an IRIX (SGI) partition table")), - { 0, NULL } - } -}; - -struct menu menu_geo = { - .callback = geo_menu_cb, - .exclude = FDISK_DISKLABEL_GPT | FDISK_DISKLABEL_BSD, - .entries = { - MENU_XSEP(N_("Geometry")), - MENU_XENT('c', N_("change number of cylinders")), - MENU_XENT('h', N_("change number of heads")), - MENU_XENT('s', N_("change number of sectors/track")), - { 0, NULL } - } -}; - -struct menu menu_gpt = { - .callback = gpt_menu_cb, - .label = FDISK_DISKLABEL_GPT, - .entries = { - MENU_XSEP(N_("GPT")), - MENU_XENT('i', N_("change disk GUID")), - MENU_XENT('n', N_("change partition name")), - MENU_XENT('u', N_("change partition UUID")), - MENU_XENT('M', N_("enter protective/hybrid MBR")), - - MENU_XSEP(""), - MENU_XENT('A', N_("toggle the legacy BIOS bootable flag")), - MENU_XENT('B', N_("toggle the no block IO protocol flag")), - MENU_XENT('R', N_("toggle the required partition flag")), - MENU_XENT('S', N_("toggle the GUID specific bits")), - - { 0, NULL } - } -}; - -struct menu menu_sun = { - .callback = sun_menu_cb, - .label = FDISK_DISKLABEL_SUN, - .entries = { - MENU_BSEP(N_("Sun")), - MENU_ENT('a', N_("toggle the read-only flag")), - MENU_ENT('c', N_("toggle the mountable flag")), - - MENU_XENT('a', N_("change number of alternate cylinders")), - MENU_XENT('e', N_("change number of extra sectors per cylinder")), - MENU_XENT('i', N_("change interleave factor")), - MENU_XENT('o', N_("change rotation speed (rpm)")), - MENU_XENT('y', N_("change number of physical cylinders")), - { 0, NULL } - } -}; - -struct menu menu_sgi = { - .callback = sgi_menu_cb, - .label = FDISK_DISKLABEL_SGI, - .entries = { - MENU_SEP(N_("SGI")), - MENU_ENT('a', N_("select bootable partition")), - MENU_ENT('b', N_("edit bootfile entry")), - MENU_ENT('c', N_("select sgi swap partition")), - MENU_ENT('i', N_("create SGI info")), - { 0, NULL } - } -}; - -struct menu menu_dos = { - .callback = dos_menu_cb, - .label = FDISK_DISKLABEL_DOS, - .entries = { - MENU_BSEP(N_("DOS (MBR)")), - MENU_ENT('a', N_("toggle a bootable flag")), - MENU_ENT('b', N_("edit nested BSD disklabel")), - MENU_ENT('c', N_("toggle the dos compatibility flag")), - - MENU_XENT('b', N_("move beginning of data in a partition")), - MENU_XENT('f', N_("fix partition order")), - MENU_XENT('i', N_("change the disk identifier")), - - MENU_XENT_NEST('M', N_("return from protective/hybrid MBR to GPT"), - FDISK_DISKLABEL_DOS, FDISK_DISKLABEL_GPT), - { 0, NULL } - } -}; - -struct menu menu_bsd = { - .callback = bsd_menu_cb, - .label = FDISK_DISKLABEL_BSD, - .entries = { - MENU_SEP(N_("BSD")), - MENU_ENT('e', N_("edit drive data")), - MENU_ENT('i', N_("install bootstrap")), - MENU_ENT('s', N_("show complete disklabel")), - MENU_ENT('x', N_("link BSD partition to non-BSD partition")), - { 0, NULL } - } -}; - -static const struct menu *menus[] = { - &menu_gpt, - &menu_sun, - &menu_sgi, - &menu_dos, - &menu_bsd, - &menu_geo, - &menu_generic, - &menu_createlabel, -}; - -static const struct menu_entry *next_menu_entry( - struct fdisk_context *cxt, - struct menu_context *mc) -{ - while (mc->menu_idx < ARRAY_SIZE(menus)) { - const struct menu *m = menus[mc->menu_idx]; - const struct menu_entry *e = &(m->entries[mc->entry_idx]); - - /* - * whole-menu filter - */ - - /* no more entries */ - if (e->title == NULL || - /* menu wanted for specified labels only */ - (m->label && cxt->label && !(m->label & cxt->label->id)) || - /* unwanted for nested PT */ - (m->nonested && cxt->parent) || - /* menu excluded for specified labels */ - (m->exclude && cxt->label && (m->exclude & cxt->label->id))) { - mc->menu_idx++; - mc->entry_idx = 0; - continue; - } - - /* - * per entry filter - */ - - /* excluded for the current label */ - if ((e->exclude && cxt->label && e->exclude & cxt->label->id) || - /* entry wanted for specified labels only */ - (e->label && cxt->label && !(e->label & cxt->label->id)) || - /* exclude non-expert entries in expect mode */ - (e->expert == 0 && fdisk_context_display_details(cxt)) || - /* nested only */ - (e->parent && (!cxt->parent || cxt->parent->label->id != e->parent)) || - /* exclude non-normal entries in normal mode */ - (e->normal == 0 && !fdisk_context_display_details(cxt))) { - - mc->entry_idx++; - continue; - } - mc->entry_idx++; - return e; - - } - return NULL; -} - -/* returns @menu and menu entry for then @key */ -static const struct menu_entry *get_fdisk_menu_entry( - struct fdisk_context *cxt, - int key, - const struct menu **menu) -{ - struct menu_context mc = MENU_CXT_EMPTY; - const struct menu_entry *e; - - while ((e = next_menu_entry(cxt, &mc))) { - if (IS_MENU_SEP(e) || e->key != key) - continue; - - if (menu) - *menu = menus[mc.menu_idx]; - return e; - } - - return NULL; -} - -static int menu_detect_collisions(struct fdisk_context *cxt) -{ - struct menu_context mc = MENU_CXT_EMPTY; - const struct menu_entry *e, *r; - - while ((e = next_menu_entry(cxt, &mc))) { - if (IS_MENU_SEP(e)) - continue; - - r = get_fdisk_menu_entry(cxt, e->key, NULL); - if (!r) { - DBG(FRONTEND, dbgprint("warning: not found " - "entry for %c", e->key)); - return -1; - } - if (r != e) { - DBG(FRONTEND, dbgprint("warning: duplicate key '%c'", - e->key)); - DBG(FRONTEND, dbgprint(" : %s", e->title)); - DBG(FRONTEND, dbgprint(" : %s", r->title)); - abort(); - } - } - - return 0; -} - -static int print_fdisk_menu(struct fdisk_context *cxt) -{ - struct menu_context mc = MENU_CXT_EMPTY; - const struct menu_entry *e; - - ON_DBG(FRONTEND, menu_detect_collisions(cxt)); - - if (fdisk_context_display_details(cxt)) - printf(_("\nHelp (expert commands):\n")); - else - printf(_("\nHelp:\n")); - - while ((e = next_menu_entry(cxt, &mc))) { - if (IS_MENU_HID(e)) - continue; /* hidden entry */ - if (IS_MENU_SEP(e) && (!e->title || !*e->title)) - printf("\n"); - else if (IS_MENU_SEP(e)) { - color_enable(UL_COLOR_BOLD); - printf("\n %s\n", _(e->title)); - color_disable(); - } else - printf(" %c %s\n", e->key, _(e->title)); - } - fputc('\n', stdout); - - if (cxt->parent) - fdisk_info(cxt, _("You're editing nested '%s' partition table, " - "primary partition table is '%s'."), - cxt->label->name, - cxt->parent->label->name); - - return 0; -} - -/* Asks for command, verify the key and perform the command or - * returns the command key if no callback for the command is - * implemented. - * - * Note that this function might exchange the context pointer to - * switch to another (nested) context. - * - * Returns: <0 on error - * 0 on success (the command performed) - * >0 if no callback (then returns the key) - */ -int process_fdisk_menu(struct fdisk_context **cxt0) -{ - struct fdisk_context *cxt = *cxt0; - const struct menu_entry *ent; - const struct menu *menu; - int key, rc; - const char *prompt; - char buf[BUFSIZ]; - - if (fdisk_context_display_details(cxt)) - prompt = _("Expert command (m for help): "); - else - prompt = _("Command (m for help): "); - - fputc('\n',stdout); - rc = get_user_reply(cxt, prompt, buf, sizeof(buf)); - if (rc) - return rc; - - key = buf[0]; - ent = get_fdisk_menu_entry(cxt, key, &menu); - if (!ent) { - fdisk_warnx(cxt, _("%c: unknown command"), key); - return -EINVAL; - } - - rc = 0; - DBG(FRONTEND, dbgprint("selected: key=%c, entry='%s'", - key, ent->title)); - - /* menu has implemented callback, use it */ - if (menu->callback) - rc = menu->callback(cxt0, menu, ent); - else { - DBG(FRONTEND, dbgprint("no callback for key '%c'", key)); - rc = -EINVAL; - } - - DBG(FRONTEND, dbgprint("process menu done [rc=%d]", rc)); - return rc; -} - -/* - * Basic fdisk actions - */ -static int generic_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - int rc = 0; - size_t n; - - /* actions shared between expert and normal mode */ - switch (ent->key) { - case 'p': - list_disk_geometry(cxt); - list_disklabel(cxt); - break; - case 'w': - rc = fdisk_write_disklabel(cxt); - if (rc) - err(EXIT_FAILURE, _("failed to write disklabel")); - if (cxt->parent) - break; /* nested PT, don't leave */ - fdisk_info(cxt, _("The partition table has been altered.")); - rc = fdisk_reread_partition_table(cxt); - if (!rc) - rc = fdisk_context_deassign_device(cxt); - /* fallthrough */ - case 'q': - fdisk_free_context(cxt); - fputc('\n', stdout); - exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE); - case 'm': - rc = print_fdisk_menu(cxt); - break; - case 'v': - rc = fdisk_verify_disklabel(cxt); - break; - } - - /* expert mode */ - if (ent->expert) { - switch (ent->key) { - case 'd': - dump_firstsector(cxt); - break; - case 'D': - dump_disklabel(cxt); - break; - case 'r': - rc = fdisk_context_enable_details(cxt, 0); - break; - } - return rc; - } - - /* normal mode */ - switch (ent->key) { - case 'd': - rc = fdisk_ask_partnum(cxt, &n, FALSE); - if (!rc) - rc = fdisk_delete_partition(cxt, n); - if (rc) - fdisk_warnx(cxt, _("Could not delete partition %zu"), n + 1); - else - fdisk_info(cxt, _("Partition %zu has been deleted."), n + 1); - break; - case 'l': - list_partition_types(cxt); - break; - case 'n': - rc = fdisk_add_partition(cxt, NULL); - break; - case 't': - change_partition_type(cxt); - break; - case 'u': - fdisk_context_set_unit(cxt, - fdisk_context_use_cylinders(cxt) ? "sectors" : - "cylinders"); - if (fdisk_context_use_cylinders(cxt)) - fdisk_info(cxt, _("Changing display/entry units to cylinders (DEPRECATED!).")); - else - fdisk_info(cxt, _("Changing display/entry units to sectors.")); - break; - case 'x': - fdisk_context_enable_details(cxt, 1); - break; - case 'r': - /* return from nested BSD to DOS */ - if (cxt->parent) { - *cxt0 = cxt->parent; - - fdisk_info(cxt, _("Leaving nested disklabel.")); - fdisk_free_context(cxt); - cxt = *cxt0; - } - break; - } - - return rc; -} - - -/* - * This is fdisk frontend for GPT specific libfdisk functions that - * are not expported by generic libfdisk API. - */ -static int gpt_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - struct fdisk_context *mbr; - size_t n; - int rc = 0; - - assert(cxt); - assert(ent); - assert(fdisk_is_disklabel(cxt, GPT)); - - DBG(FRONTEND, dbgprint("enter GPT menu")); - - if (ent->expert) { - switch (ent->key) { - case 'i': - return fdisk_set_disklabel_id(cxt); - case 'M': - mbr = fdisk_new_nested_context(cxt, "dos"); - if (!mbr) - return -ENOMEM; - *cxt0 = cxt = mbr; - fdisk_context_enable_details(cxt, 1); /* keep us in expert mode */ - fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, - _("Entering protective/hybrid MBR disklabel.")); - return 0; - } - - /* actions where is necessary partnum */ - rc = fdisk_ask_partnum(cxt, &n, FALSE); - if (rc) - return rc; - - switch(ent->key) { - case 'u': - rc = fdisk_gpt_partition_set_uuid(cxt, n); - break; - case 'n': - rc = fdisk_gpt_partition_set_name(cxt, n); - break; - case 'A': - rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_LEGACYBOOT); - break; - case 'B': - rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_NOBLOCK); - break; - case 'R': - rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_REQUIRED); - break; - case 'S': - rc = fdisk_partition_toggle_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC); - break; - } - } - return rc; -} - - -/* - * This is fdisk frontend for MBR specific libfdisk functions that - * are not expported by generic libfdisk API. - */ -static int dos_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - int rc = 0; - - DBG(FRONTEND, dbgprint("enter DOS menu")); - - if (!ent->expert) { - switch (ent->key) { - case 'a': - { - size_t n; - rc = fdisk_ask_partnum(cxt, &n, FALSE); - if (!rc) - rc = fdisk_partition_toggle_flag(cxt, n, DOS_FLAG_ACTIVE); - break; - } - case 'b': - { - struct fdisk_context *bsd - = fdisk_new_nested_context(cxt, "bsd"); - if (!bsd) - return -ENOMEM; - if (!fdisk_dev_has_disklabel(bsd)) - rc = fdisk_create_disklabel(bsd, "bsd"); - if (rc) - fdisk_free_context(bsd); - else { - *cxt0 = cxt = bsd; - fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, - _("Entering nested BSD disklabel.")); - } - break; - } - case 'c': - toggle_dos_compatibility_flag(cxt); - break; - } - return rc; - } - - /* expert mode */ - switch (ent->key) { - case 'b': - { - size_t n; - rc = fdisk_ask_partnum(cxt, &n, FALSE); - if (!rc) - rc = fdisk_dos_move_begin(cxt, n); - break; - } - case 'f': - rc = fdisk_dos_fix_order(cxt); - break; - case 'i': - rc = fdisk_set_disklabel_id(cxt); - break; - case 'M': - /* return from nested MBR to GPT */ - if (cxt->parent) { - *cxt0 = cxt->parent; - - fdisk_info(cxt, _("Leaving nested disklabel.")); - fdisk_free_context(cxt); - cxt = *cxt0; - } - break; - } - return rc; -} - -static int sun_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - int rc = 0; - - DBG(FRONTEND, dbgprint("enter SUN menu")); - - assert(cxt); - assert(ent); - assert(fdisk_is_disklabel(cxt, SUN)); - - DBG(FRONTEND, dbgprint("enter SUN menu")); - - /* normal mode */ - if (!ent->expert) { - size_t n; - - rc = fdisk_ask_partnum(cxt, &n, FALSE); - if (rc) - return rc; - switch (ent->key) { - case 'a': - rc = fdisk_partition_toggle_flag(cxt, n, SUN_FLAG_RONLY); - break; - case 'c': - rc = fdisk_partition_toggle_flag(cxt, n, SUN_FLAG_UNMNT); - break; - } - return rc; - } - - /* expert mode */ - switch (ent->key) { - case 'a': - rc = fdisk_sun_set_alt_cyl(cxt); - break; - case 'e': - rc = fdisk_sun_set_xcyl(cxt); - break; - case 'i': - rc = fdisk_sun_set_ilfact(cxt); - break; - case 'o': - rc = fdisk_sun_set_rspeed(cxt); - break; - case 'y': - rc = fdisk_sun_set_pcylcount(cxt); - break; - } - return rc; -} - -static int sgi_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - int rc = -EINVAL; - size_t n = 0; - - DBG(FRONTEND, dbgprint("enter SGI menu")); - - assert(cxt); - assert(ent); - assert(fdisk_is_disklabel(cxt, SGI)); - - if (ent->expert) - return rc; - - switch (ent->key) { - case 'a': - rc = fdisk_ask_partnum(cxt, &n, FALSE); - if (!rc) - rc = fdisk_partition_toggle_flag(cxt, n, SGI_FLAG_BOOT); - break; - case 'b': - fdisk_sgi_set_bootfile(cxt); - break; - case 'c': - rc = fdisk_ask_partnum(cxt, &n, FALSE); - if (!rc) - rc = fdisk_partition_toggle_flag(cxt, n, SGI_FLAG_SWAP); - break; - case 'i': - rc = fdisk_sgi_create_info(cxt); - break; - } - - return rc; -} - -/* - * This is fdisk frontend for BSD specific libfdisk functions that - * are not expported by generic libfdisk API. - */ -static int bsd_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - int rc = 0, org; - - assert(cxt); - assert(ent); - assert(fdisk_is_disklabel(cxt, BSD)); - - DBG(FRONTEND, dbgprint("enter BSD menu")); - - switch(ent->key) { - case 'e': - rc = fdisk_bsd_edit_disklabel(cxt); - break; - case 'i': - rc = fdisk_bsd_write_bootstrap(cxt); - break; - case 's': - org = fdisk_context_display_details(cxt); - - fdisk_context_enable_details(cxt, 1); - list_disklabel(cxt); - fdisk_context_enable_details(cxt, org); - break; - case 'x': - rc = fdisk_bsd_link_partition(cxt); - break; - } - return rc; -} - -/* C/H/S commands */ -static int geo_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - int rc = -EINVAL; - uintmax_t c = 0, h = 0, s = 0; - - DBG(FRONTEND, dbgprint("enter GEO menu")); - - assert(cxt); - assert(ent); - - switch (ent->key) { - case 'c': - rc = fdisk_ask_number(cxt, 1, cxt->geom.cylinders, - 1048576, _("Number of cylinders"), &c); - break; - case 'h': - rc = fdisk_ask_number(cxt, 1, cxt->geom.heads, - 256, _("Number of heads"), &h); - break; - case 's': - rc = fdisk_ask_number(cxt, 1, cxt->geom.sectors, - 63, _("Number of sectors"), &s); - break; - } - - if (!rc) - fdisk_override_geometry(cxt, c, h, s); - return rc; -} - -static int createlabel_menu_cb(struct fdisk_context **cxt0, - const struct menu *menu __attribute__((__unused__)), - const struct menu_entry *ent) -{ - struct fdisk_context *cxt = *cxt0; - int rc = -EINVAL; - - DBG(FRONTEND, dbgprint("enter Create label menu")); - - assert(cxt); - assert(ent); - - if (ent->expert) { - switch (ent->key) { - case 'g': - /* Deprecated, use 'G' in main menu, just for backward - * compatibility only. */ - rc = fdisk_create_disklabel(cxt, "sgi"); - break; - } - return rc; - } - - switch (ent->key) { - case 'g': - fdisk_create_disklabel(cxt, "gpt"); - break; - case 'G': - fdisk_create_disklabel(cxt, "sgi"); - break; - case 'o': - fdisk_create_disklabel(cxt, "dos"); - break; - case 's': - fdisk_create_disklabel(cxt, "sun"); - break; - } - return rc; -} diff --git a/fdisks/fdisk.8 b/fdisks/fdisk.8 deleted file mode 100644 index 0a3c1e706..000000000 --- a/fdisks/fdisk.8 +++ /dev/null @@ -1,272 +0,0 @@ -.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) -.\" Copyright 1998 Andries E. Brouwer (aeb@cwi.nl) -.\" Copyright 2012 Davidlohr Bueso -.\" Copyright (C) 2013 Karel Zak -.\" May be distributed under the GNU General Public License -.TH FDISK 8 "September 2013" "util-linux" "System Administration" - -.SH NAME -fdisk \- manipulate disk partition table - -.SH SYNOPSIS -.B fdisk -.RB [ options ] -.I device -.sp -.B fdisk \-l -.RI [ device ...] - -.SH DESCRIPTION -.B fdisk -is a dialog-driven program for creation and manipulation of partition tables. -It understands GPT, MBR, Sun, SGI and BSD partition tables. - -Block devices can be divided into one or more logical disks called -.IR partitions . -This division is recorded in the -.IR "partition table" , -usually found in sector 0 of the disk. -(In the BSD world one talks about `disk slices' and a `disklabel'.) - -All partitioning is driven by device I/O limits (the topology) by default. -.B fdisk -is able to optimize the disk layout for a 4K-sector size and use an alignment offset on -modern devices for MBR and GPT. It is always a good idea to follow \fBfdisk\fR's defaults -as the default values (e.g. first and last partition sectors) and partition -sizes specified by the +{M,G,...} notation are always aligned according -to the device properties. - -Note that -.BR partx (8) -provides a rich interface for scripts to print disk layouts, -.B fdisk -is mostly designed for humans. Backward compatibility in the output of -.B fdisk -is not guaranteed. The input (the commands) should always be backward compatible. - -.SH OPTIONS -.TP -.BI "\-b " sectorsize -Specify the sector size of the disk. Valid values are 512, 1024, 2048, and 4096. -(Recent kernels know the sector size. Use this option only on old kernels or -to override the kernel's ideas.) Since util-linux-2.17, \fBfdisk\fR differentiates -between logical and physical sector size. This option changes both sector sizes to -.IB sectorsize . -.TP -.BR "\-c"[=\fImode\fR] -Specify the compatibility mode, 'dos' or 'nondos'. The default is non-DOS -mode. For backward compatibility, it is possible to use the option without -the \fImode\fR argument -- then the default is used. Note that the optional -\fImode\fR argument cannot be separated from the \fB-c\fR option by a space, -the correct form is for example '-c=dos'. This option is DEPRECATED. -.TP -.BI "\-C " cylinders -Specify the number of cylinders of the disk. -I have no idea why anybody would want to do so. This option is DEPRECATED. -.TP -.BI "\-H " heads -Specify the number of heads of the disk. (Not the physical number, -of course, but the number used for partition tables.) -Reasonable values are 255 and 16. This option is DEPRECATED. -.TP -.BI "\-S " sectors -Specify the number of sectors per track of the disk. -(Not the physical number, of course, but the number used for -partition tables.) -A reasonable value is 63. This option is DEPRECATED. -.TP -.BI \-h -Display a help text and exit. -.TP -.BR "\-L"[=\fIwhen\fR] -Colorize the output in interactive mode. The optional argument \fIwhen\fP can -be \fBauto\fR, \fBnever\fR or \fBalways\fR. The default is \fBauto\fR. -.TP -.B \-l -List the partition tables for the specified devices and then exit. -If no devices are given, those mentioned in -.I /proc/partitions -(if that file exists) are used. -.TP -.BI "\-s " partition... -Print the size (in blocks) of each given partition. This option is DEPRECATED -in favour of -.BR blockdev (1). -.TP -.BI "\-t " type -Enable support only for disklabels of the specified \fItype\fP, and disable -support for all other types. -This is necessary for example to access a protective or hybrid MBR on devices -with GPT. -.TP -.BR "\-u"[=\fIunit\fR] -When listing partition tables, show sizes in 'sectors' or in 'cylinders'. The -default is to show sizes in sectors. For backward compatibility, it is possible -to use the option without the \fIunit\fR argument -- then the default is used. -Note that the optional \fIunit\fR argument cannot be separated from the \fB-u\fR -option by a space, the correct form is for example '-u=cylinders'. -.TP -.B \-v -Display version information and exit. - -.SH DEVICES -The -.I device -is usually /dev/sda, /dev/sdb or so. A device name refers to the entire disk. -Old systems without libata (a library used inside the Linux kernel to support -ATA host controllers and devices) make a difference between IDE and SCSI disks. -In such cases the device name will be /dev/hd* (IDE) or /dev/sd* (SCSI). - -The -.I partition -is a device name followed by a partition number. For example, /dev/sda1 is the -first partition on the first hard disk in the system. See also Linux kernel -documentation (the Documentation/devices.txt file). - -.SH DISK LABELS -.B GPT (GUID Partition Table) -.RS -GPT is modern standard for the layout of the partition table. GPT uses 64-bit -logical block addresses, checksums, UUIDs and names for partitions and an -unlimited number of partitions (although the number of partitions is -usually restricted to 128 in many partitioning tools). - -Note that the first sector is still reserved for a -.B protective MBR -in the GPT specification. It prevents MBR-only partitioning tools -from mis-recognizing and overwriting GPT disks. - -GPT is always a better choice than MBR, especially on modern hardware with a UEFI -boot loader. -.RE - -.B DOS-type (MBR) -.RS -A DOS-type partition table can describe an unlimited number of partitions. In sector 0 -there is room for the description of 4 partitions (called `primary'). One of -these may be an extended partition; this is a box holding logical partitions, -with descriptors found in a linked list of sectors, each preceding the -corresponding logical partitions. The four primary partitions, present or not, -get numbers 1-4. Logical partitions are numbered starting from 5. - -In a DOS-type partition table the starting offset and the size of each -partition is stored in two ways: as an absolute number of sectors (given in 32 -bits), and as a -.B Cylinders/Heads/Sectors -triple (given in 10+8+6 bits). The former is OK -- with 512-byte sectors this -will work up to 2 TB. The latter has two problems. First, these C/H/S fields -can be filled only when the number of heads and the number of sectors per track -are known. And second, even if we know what these numbers should be, the 24 -bits that are available do not suffice. DOS uses C/H/S only, Windows uses -both, Linux never uses C/H/S. The -.B C/H/S addressing is deprecated -and may be unsupported in some later fdisk version. - -.B Please, read the DOS-mode section if you want DOS-compatible partitions. -.B fdisk -does not care about cylinder boundaries by default. -.RE - -.B BSD/Sun-type -.RS -A BSD/Sun disklabel can describe 8 partitions, the third of which should be a `whole -disk' partition. Do not start a partition that actually uses its first sector -(like a swap partition) at cylinder 0, since that will destroy the disklabel. -Note that a -.B BSD label -is usually nested within a DOS partition. -.RE - -.B IRIX/SGI-type -.RS -An IRIX/SGI disklabel can describe 16 partitions, the eleventh of which should be an entire -`volume' partition, while the ninth should be labeled `volume header'. The -volume header will also cover the partition table, i.e., it starts at block -zero and extends by default over five cylinders. The remaining space in the -volume header may be used by header directory entries. No partitions may -overlap with the volume header. Also do not change its type or make some -filesystem on it, since you will lose the partition table. Use this type of -label only when working with Linux on IRIX/SGI machines or IRIX/SGI disks under -Linux. -.RE - -A sync() and an ioctl(BLKRRPART) (rereading the partition table from disk) -are performed before exiting when the partition table has been updated. - -.SH "DOS mode and DOS 6.x WARNING" -.B Note that all this is deprecated. You don't have to care about things like -.B geometry and cylinders on modern operating systems. If you really want -.B DOS-compatible partitioning then you have to enable DOS mode and cylinder -.B units by using the '-c=dos -u=cylinders' fdisk command-line options. - -The DOS 6.x FORMAT command looks for some information in the first sector of -the data area of the partition, and treats this information as more reliable -than the information in the partition table. DOS FORMAT expects DOS FDISK to -clear the first 512 bytes of the data area of a partition whenever a size -change occurs. DOS FORMAT will look at this extra information even if the /U -flag is given -- we consider this a bug in DOS FORMAT and DOS FDISK. - -The bottom line is that if you use \fBfdisk\fR or \fBcfdisk\fR to change the -size of a DOS partition table entry, then you must also use -.BR dd "(1) to " "zero the first 512 bytes" -of that partition before using DOS FORMAT to format the partition. For -example, if you were using \fBfdisk\fR to make a DOS partition table entry for -/dev/sda1, then (after exiting \fBfdisk\fR and rebooting Linux so that the -partition table information is valid) you would use the command "dd -if=/dev/zero of=/dev/sda1 bs=512 count=1" to zero the first 512 bytes of the -partition. - -.B fdisk -usually obtains the disk geometry automatically. This is not necessarily the -physical disk geometry (indeed, modern disks do not really have anything like a -physical geometry, certainly not something that can be described in the simplistic -Cylinders/Heads/Sectors form), but it is the disk geometry that MS-DOS uses for -the partition table. - -Usually all goes well by default, and there are no problems if Linux is the -only system on the disk. However, if the disk has to be shared with other -operating systems, it is often a good idea to let an fdisk from another -operating system make at least one partition. When Linux boots it looks at the -partition table, and tries to deduce what (fake) geometry is required for good -cooperation with other systems. - -Whenever a partition table is printed out in DOS mode, a consistency check is -performed on the partition table entries. This check verifies that the -physical and logical start and end points are identical, and that each -partition starts and ends on a cylinder boundary (except for the first -partition). - -Some versions of MS-DOS create a first partition which does not begin -on a cylinder boundary, but on sector 2 of the first cylinder. -Partitions beginning in cylinder 1 cannot begin on a cylinder boundary, but -this is unlikely to cause difficulty unless you have OS/2 on your machine. - -For best results, you should always use an OS-specific partition table -program. For example, you should make DOS partitions with the DOS FDISK -program and Linux partitions with the Linux fdisk or Linux cfdisk programs. - -.SH AUTHORS -.MT kzak@redhat.com -Karel Zak -.ME -.br -.MT dave@gnu.org -Davidlohr Bueso -.ME -.br -.PP -The original version was written by -Andries E. Brouwer, A. V. Le Blanc and others. - -.SH ENVIRONMENT -.IP "Setting LIBFDISK_DEBUG=0xffff enables debug output." - -.SH "SEE ALSO" -.BR cfdisk (8), -.BR sfdisk (8), -.BR mkfs (8), -.BR partx (8) - -.SH AVAILABILITY -The fdisk command is part of the util-linux package and is available from -ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/fdisks/fdisk.c b/fdisks/fdisk.c deleted file mode 100644 index e003baf64..000000000 --- a/fdisks/fdisk.c +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) - * Copyright (C) 2012 Davidlohr Bueso - * - * Copyright (C) 2007-2013 Karel Zak - * - * 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 1 or - * (at your option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "c.h" -#include "xalloc.h" -#include "all-io.h" -#include "nls.h" -#include "rpmatch.h" -#include "blkdev.h" -#include "mbsalign.h" -#include "fdisk.h" -#include "wholedisk.h" -#include "pathnames.h" -#include "canonicalize.h" -#include "strutils.h" -#include "closestream.h" - -#include "pt-sun.h" /* to toggle flags */ - -#ifdef HAVE_LINUX_COMPILER_H -# include -#endif -#ifdef HAVE_LINUX_BLKPG_H -# include -#endif - -static void __attribute__ ((__noreturn__)) usage(FILE *out) -{ - fputs(USAGE_HEADER, out); - - fprintf(out, - _(" %1$s [options] change partition table\n" - " %1$s [options] -l [] list partition table(s)\n"), - program_invocation_short_name); - - fputs(USAGE_OPTIONS, out); - fputs(_(" -b sector size (512, 1024, 2048 or 4096)\n"), out); - fputs(_(" -c[=] compatible mode: 'dos' or 'nondos' (default)\n"), out); - fputs(_(" -h print this help text\n"), out); - fputs(_(" -c[=] compatible mode: 'dos' or 'nondos' (default)\n"), out); - fputs(_(" -L[=] colorize output (auto, always or never)\n"), out); - fputs(_(" -t force fdisk to recognize specified partition table type only\n"), out); - fputs(_(" -u[=] display units: 'cylinders' or 'sectors' (default)\n"), out); - fputs(_(" -v print program version\n"), out); - fputs(_(" -C specify the number of cylinders\n"), out); - fputs(_(" -H specify the number of heads\n"), out); - fputs(_(" -S specify the number of sectors per track\n"), out); - - fprintf(out, USAGE_MAN_TAIL("fdisk(8)")); - exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); -} - - - -int get_user_reply(struct fdisk_context *cxt, const char *prompt, - char *buf, size_t bufsz) -{ - char *p; - size_t sz; - - do { - fputs(prompt, stdout); - fflush(stdout); - - if (!fgets(buf, bufsz, stdin)) { - if (fdisk_label_is_changed(cxt->label)) { - fprintf(stderr, _("\nDo you really want to quit? ")); - - if (fgets(buf, bufsz, stdin) && !rpmatch(buf)) - continue; - } - fdisk_free_context(cxt); - exit(EXIT_FAILURE); - } else - break; - } while (1); - - for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */ - - if (p > buf) - memmove(buf, p, p - buf); /* remove blank space */ - sz = strlen(buf); - if (sz && *(buf + sz - 1) == '\n') - *(buf + sz - 1) = '\0'; - - DBG(ASK, dbgprint("user's reply: >>>%s<<<", buf)); - return 0; -} - -static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask, - char *buf, size_t bufsz) - -{ - const char *q = fdisk_ask_get_query(ask); - int dft = fdisk_ask_menu_get_default(ask); - - if (q) { - fputs(q, stdout); /* print header */ - fputc('\n', stdout); - } - - do { - char prompt[128]; - int key, c; - const char *name, *desc; - size_t i = 0; - - /* print menu items */ - while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) - fprintf(stdout, " %c %s (%s)\n", key, name, desc); - - /* ask for key */ - snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft); - get_user_reply(cxt, prompt, buf, bufsz); - if (!*buf) { - fdisk_info(cxt, _("Using default response %c."), dft); - c = dft; - } else - c = tolower(buf[0]); - - /* check result */ - i = 0; - while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) { - if (c == key) { - fdisk_ask_menu_set_result(ask, c); - return 0; /* success */ - } - } - fdisk_warnx(cxt, _("Value out of range.")); - } while (1); - - return -EINVAL; -} - - -#define tochar(num) ((int) ('a' + num - 1)) -static int ask_number(struct fdisk_context *cxt, - struct fdisk_ask *ask, - char *buf, size_t bufsz) -{ - char prompt[128] = { '\0' }; - const char *q = fdisk_ask_get_query(ask); - const char *range = fdisk_ask_number_get_range(ask); - - uint64_t dflt = fdisk_ask_number_get_default(ask), - low = fdisk_ask_number_get_low(ask), - high = fdisk_ask_number_get_high(ask); - int inchar = fdisk_ask_number_inchars(ask); - - assert(q); - - DBG(ASK, dbgprint("asking for number " - "['%s', <%ju,%ju>, default=%ju, range: %s]", - q, low, high, dflt, range)); - - if (range && dflt >= low && dflt <= high) { - if (inchar) - snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "), - q, range, tochar(dflt)); - else - snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), - q, range, dflt); - - } else if (dflt >= low && dflt <= high) { - if (inchar) - snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "), - q, tochar(low), tochar(high), tochar(dflt)); - else - snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), - q, low, high, dflt); - } else if (inchar) - snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "), - q, tochar(low), tochar(high)); - else - snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), - q, low, high); - - do { - int rc = get_user_reply(cxt, prompt, buf, bufsz); - uint64_t num; - - if (rc) - return rc; - if (!*buf && dflt >= low && dflt <= high) - return fdisk_ask_number_set_result(ask, dflt); - - if (isdigit_string(buf)) { - char *end; - - errno = 0; - num = strtoumax(buf, &end, 10); - if (errno || buf == end || (end && *end)) - continue; - } else if (inchar && isalpha(*buf)) { - num = tolower(*buf) - 'a' + 1; - } else - rc = -EINVAL; - - if (rc == 0 && num >= low && num <= high) - return fdisk_ask_number_set_result(ask, num); - - fdisk_warnx(cxt, _("Value out of range.")); - } while (1); - - return -1; -} - -static int ask_offset(struct fdisk_context *cxt, - struct fdisk_ask *ask, - char *buf, size_t bufsz) -{ - char prompt[128] = { '\0' }; - const char *q = fdisk_ask_get_query(ask); - const char *range = fdisk_ask_number_get_range(ask); - - uint64_t dflt = fdisk_ask_number_get_default(ask), - low = fdisk_ask_number_get_low(ask), - high = fdisk_ask_number_get_high(ask), - base = fdisk_ask_number_get_base(ask); - - assert(q); - - DBG(ASK, dbgprint("asking for offset ['%s', <%ju,%ju>, base=%ju, default=%ju, range: %s]", - q, low, high, base, dflt, range)); - - if (range && dflt >= low && dflt <= high) - snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), q, range, dflt); - else if (dflt >= low && dflt <= high) - snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), q, low, high, dflt); - else - snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), q, low, high); - - do { - uint64_t num = 0; - char sig = 0, *p; - int pwr = 0; - - int rc = get_user_reply(cxt, prompt, buf, bufsz); - if (rc) - return rc; - if (!*buf && dflt >= low && dflt <= high) - return fdisk_ask_number_set_result(ask, dflt); - - p = buf; - if (*p == '+' || *p == '-') { - sig = *buf; - p++; - } - - rc = parse_size(p, &num, &pwr); - if (rc) - continue; - DBG(ASK, dbgprint("parsed size: %ju", num)); - if (sig && pwr) { - /* +{size}{K,M,...} specified, the "num" is in bytes */ - uint64_t unit = fdisk_ask_number_get_unit(ask); - num += unit/2; /* round */ - num /= unit; - } - if (sig == '+') - num += base; - else if (sig == '-') - num = base - num; - - DBG(ASK, dbgprint("final offset: %ju [sig: %c, power: %d, %s]", - num, sig, pwr, - sig ? "relative" : "absolute")); - if (num >= low && num <= high) { - if (sig) - fdisk_ask_number_set_relative(ask, 1); - return fdisk_ask_number_set_result(ask, num); - } - fdisk_warnx(cxt, _("Value out of range.")); - } while (1); - - return -1; -} - -static unsigned int info_count; - -static void fputs_info(struct fdisk_ask *ask, FILE *out, char *buf, size_t bufsz) -{ - const char *msg; - unsigned int flags; - - assert(ask); - - msg = fdisk_ask_print_get_mesg(ask); - flags = fdisk_ask_get_flags(ask); - - if (!msg) - return; - if (info_count == 1) - fputc('\n', out); - if (flags == 0 || !colors_wanted()) - goto simple; - - if (flags & FDISK_INFO_COLON) { - size_t sz; - char *sep = _(": "); - char *p = strstr(msg, sep); - - if (!p) - goto simple; - - sz = strlen(sep); - strncpy(buf, msg, bufsz); - buf[p - msg + sz] = '\0'; - - color_enable(UL_COLOR_BROWN); - fputs(buf, out); - color_disable(); - fputs(p + sz, out); - - } else if (flags & FDISK_INFO_SUCCESS) { - color_enable(UL_COLOR_BOLD); - fputs(msg, out); - color_disable(); - } else { -simple: - fputs(msg, out); - } - fputc('\n', out); -} - -int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, - void *data __attribute__((__unused__))) -{ - int rc = 0; - char buf[BUFSIZ]; - - assert(cxt); - assert(ask); - - if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO) - info_count = 0; - - switch(fdisk_ask_get_type(ask)) { - case FDISK_ASKTYPE_MENU: - return ask_menu(cxt, ask, buf, sizeof(buf)); - case FDISK_ASKTYPE_NUMBER: - return ask_number(cxt, ask, buf, sizeof(buf)); - case FDISK_ASKTYPE_OFFSET: - return ask_offset(cxt, ask, buf, sizeof(buf)); - case FDISK_ASKTYPE_INFO: - info_count++; - fputs_info(ask, stdout, buf, sizeof(buf)); - break; - case FDISK_ASKTYPE_WARNX: - color_fenable(UL_COLOR_RED, stderr); - fputs(fdisk_ask_print_get_mesg(ask), stderr); - color_fdisable(stderr); - fputc('\n', stderr); - break; - case FDISK_ASKTYPE_WARN: - color_fenable(UL_COLOR_RED, stderr); - fputs(fdisk_ask_print_get_mesg(ask), stderr); - errno = fdisk_ask_print_get_errno(ask); - fprintf(stderr, ": %m\n"); - color_fdisable(stderr); - break; - case FDISK_ASKTYPE_YESNO: - fputc('\n', stdout); - fputs(fdisk_ask_get_query(ask), stdout); - rc = get_user_reply(cxt, _(" [Y]es/[N]o: "), buf, sizeof(buf)); - if (rc == 0) - fdisk_ask_yesno_set_result(ask, rpmatch(buf)); - DBG(ASK, dbgprint("yes-no ask: reply '%s' [rc=%d]", buf, rc)); - break; - case FDISK_ASKTYPE_STRING: - { - char prmt[BUFSIZ]; - snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask)); - fputc('\n', stdout); - rc = get_user_reply(cxt, prmt, buf, sizeof(buf)); - if (rc == 0) - fdisk_ask_string_set_result(ask, xstrdup(buf)); - DBG(ASK, dbgprint("string ask: reply '%s' [rc=%d]", buf, rc)); - break; - } - default: - warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask)); - return -EINVAL; - } - return rc; -} - -struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt) -{ - const char *q; - - if (!cxt || !cxt->label || !cxt->label->nparttypes) - return NULL; - - q = fdisk_is_parttype_string(cxt) ? - _("Partition type (type L to list all types): ") : - _("Hex code (type L to list all codes): "); - do { - char buf[256]; - int rc = get_user_reply(cxt, q, buf, sizeof(buf)); - - if (rc) - break; - - if (buf[1] == '\0' && toupper(*buf) == 'L') - list_partition_types(cxt); - else if (*buf) - return fdisk_parse_parttype(cxt, buf); - } while (1); - - return NULL; -} - -void list_partition_types(struct fdisk_context *cxt) -{ - struct fdisk_parttype *types; - size_t ntypes = 0; - - if (!cxt || !cxt->label || !cxt->label->parttypes) - return; - - types = cxt->label->parttypes; - ntypes = cxt->label->nparttypes; - - if (types[0].typestr == NULL) { - /* - * Prints in 4 columns in format - */ - size_t last[4], done = 0, next = 0, size; - int i; - - size = ntypes; - if (types[ntypes - 1].name == NULL) - size--; - - for (i = 3; i >= 0; i--) - last[3 - i] = done += (size + i - done) / (i + 1); - i = done = 0; - - do { - #define NAME_WIDTH 15 - char name[NAME_WIDTH * MB_LEN_MAX]; - size_t width = NAME_WIDTH; - struct fdisk_parttype *t = &types[next]; - size_t ret; - - if (t->name) { - printf("%c%2x ", i ? ' ' : '\n', t->type); - ret = mbsalign(_(t->name), name, sizeof(name), - &width, MBS_ALIGN_LEFT, 0); - - if (ret == (size_t)-1 || ret >= sizeof(name)) - printf("%-15.15s", _(t->name)); - else - fputs(name, stdout); - } - - next = last[i++] + done; - if (i > 3 || next >= last[i]) { - i = 0; - next = ++done; - } - } while (done < last[0]); - - } else { - /* - * Prints 1 column in format - */ - struct fdisk_parttype *t; - size_t i; - - for (i = 0, t = types; t && i < ntypes; t++, i++) { - if (t->name) - printf("%3zu %-30s %s\n", i + 1, - t->name, t->typestr); - } - } - putchar('\n'); -} - -void toggle_dos_compatibility_flag(struct fdisk_context *cxt) -{ - struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos"); - int flag; - - if (!lb) - return; - - flag = !fdisk_dos_is_compatible(lb); - fdisk_info(cxt, flag ? - _("DOS Compatibility flag is set (DEPRECATED!)") : - _("DOS Compatibility flag is not set")); - - fdisk_dos_enable_compatible(lb, flag); - - if (fdisk_is_disklabel(cxt, DOS)) - fdisk_reset_alignment(cxt); /* reset the current label */ -} - -void change_partition_type(struct fdisk_context *cxt) -{ - size_t i; - struct fdisk_parttype *t = NULL; - struct fdisk_partition *pa = NULL; - const char *old = NULL; - - assert(cxt); - assert(cxt->label); - - if (fdisk_ask_partnum(cxt, &i, FALSE)) - return; - - if (fdisk_get_partition(cxt, i, &pa)) { - fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1); - return; - } - - t = (struct fdisk_parttype *) fdisk_partition_get_type(pa); - old = t ? t->name : _("Unknown"); - - do { - t = ask_partition_type(cxt); - } while (!t); - - if (fdisk_set_partition_type(cxt, i, t) == 0) - fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, - _("Changed type of partition '%s' to '%s'."), - old, t ? t->name : _("Unknown")); - else - fdisk_info(cxt, - _("Type of partition %zu is unchanged: %s."), - i + 1, old); - - fdisk_unref_partition(pa); -} - -void list_disk_geometry(struct fdisk_context *cxt) -{ - char *id = NULL; - uint64_t bytes = cxt->total_sectors * cxt->sector_size; - char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE - | SIZE_SUFFIX_3LETTER, bytes); - - fdisk_colon(cxt, _("Disk %s: %s, %ju bytes, %ju sectors"), - cxt->dev_path, strsz, - bytes, (uintmax_t) cxt->total_sectors); - free(strsz); - - if (fdisk_require_geometry(cxt) || fdisk_context_use_cylinders(cxt)) - fdisk_colon(cxt, _("Geometry: %d heads, %llu sectors/track, %llu cylinders"), - cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders); - - fdisk_colon(cxt, _("Units: %s of %d * %ld = %ld bytes"), - fdisk_context_get_unit(cxt, PLURAL), - fdisk_context_get_units_per_sector(cxt), - cxt->sector_size, - fdisk_context_get_units_per_sector(cxt) * cxt->sector_size); - - fdisk_colon(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"), - cxt->sector_size, cxt->phy_sector_size); - fdisk_colon(cxt, _("I/O size (minimum/optimal): %lu bytes / %lu bytes"), - cxt->min_io_size, cxt->io_size); - if (cxt->alignment_offset) - fdisk_colon(cxt, _("Alignment offset: %lu bytes"), - cxt->alignment_offset); - if (fdisk_dev_has_disklabel(cxt)) - fdisk_colon(cxt, _("Disklabel type: %s"), cxt->label->name); - - if (fdisk_get_disklabel_id(cxt, &id) == 0 && id) - fdisk_colon(cxt, _("Disk identifier: %s"), id); -} - -void list_disklabel(struct fdisk_context *cxt) -{ - struct fdisk_table *tb = NULL; - char *str; - - /* print label specific stuff by libfdisk FDISK_ASK_INFO API */ - fdisk_list_disklabel(cxt); - - /* print partitions */ - if (fdisk_get_partitions(cxt, &tb)) - return; - if (fdisk_table_to_string(tb, cxt, NULL, 0, &str) == 0) { - fputc('\n', stdout); - if (str && *str) - fputs(str, stdout); - } - fdisk_unref_table(tb); -} - -static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz) -{ - size_t next; - const unsigned char *p0 = buf + i; - - for (next = i + 16; next < sz; next += 16) { - if (memcmp(p0, buf + next, 16) != 0) - break; - } - - return next == i + 16 ? i : next; -} - -static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all) -{ - size_t i, l, next = 0; - - if (!buf) - return; - for (i = 0, l = 0; i < sz; i++, l++) { - if (l == 0) { - if (all == 0 && !next) - next = skip_empty(buf, i, sz); - printf("%08jx ", base + i); - } - printf(" %02x", buf[i]); - if (l == 7) /* words separator */ - fputs(" ", stdout); - else if (l == 15) { - fputc('\n', stdout); /* next line */ - l = -1; - if (next > i) { - printf("*\n"); - i = next - 1; - } - next = 0; - } - } - if (l > 0) - printf("\n"); -} - -static void dump_blkdev(struct fdisk_context *cxt, const char *name, - off_t offset, size_t size, int all) -{ - fdisk_colon(cxt, _("\n%s: offset = %ju, size = %zu bytes."), - name, offset, size); - - if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1) - fdisk_warn(cxt, _("cannot seek")); - else { - unsigned char *buf = xmalloc(size); - - if (read_all(cxt->dev_fd, (char *) buf, size) != (ssize_t) size) - fdisk_warn(cxt, _("cannot read")); - else - dump_buffer(offset, buf, size, all); - free(buf); - } -} - -void dump_firstsector(struct fdisk_context *cxt) -{ - int all = !isatty(STDOUT_FILENO); - - assert(cxt); - assert(cxt->label); - - dump_blkdev(cxt, _("First sector"), 0, cxt->sector_size, all); -} - -void dump_disklabel(struct fdisk_context *cxt) -{ - int all = !isatty(STDOUT_FILENO); - int i = 0; - const char *name = NULL; - off_t offset = 0; - size_t size = 0; - - assert(cxt); - assert(cxt->label); - - while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size) - dump_blkdev(cxt, name, offset, size, all); -} - -static int is_ide_cdrom_or_tape(char *device) -{ - int fd, ret; - - if ((fd = open(device, O_RDONLY)) < 0) - return 0; - ret = blkdev_is_cdrom(fd); - - close(fd); - return ret; -} - -static void print_device_pt(struct fdisk_context *cxt, char *device) -{ - if (fdisk_context_assign_device(cxt, device, 1) != 0) /* read-only */ - err(EXIT_FAILURE, _("cannot open %s"), device); - - list_disk_geometry(cxt); - - if (fdisk_dev_has_disklabel(cxt)) - list_disklabel(cxt); - fputc('\n', stdout); -} - -static void print_all_devices_pt(struct fdisk_context *cxt) -{ - FILE *f; - char line[128 + 1]; - - f = fopen(_PATH_PROC_PARTITIONS, "r"); - if (!f) { - warn(_("cannot open %s"), _PATH_PROC_PARTITIONS); - return; - } - - DBG(FRONTEND, dbgprint("reading "_PATH_PROC_PARTITIONS)); - - while (fgets(line, sizeof(line), f)) { - char ptname[128 + 1], devname[256]; - - if (sscanf(line, " %*d %*d %*d %128[^\n ]", ptname) != 1) - continue; - - snprintf(devname, sizeof(devname), "/dev/%s", ptname); - - DBG(FRONTEND, dbgprint("listing %s", devname)); - - if (is_whole_disk(devname)) { - char *cn = canonicalize_path(devname); - if (cn) { - if (!is_ide_cdrom_or_tape(cn)) - print_device_pt(cxt, cn); - free(cn); - } - } - } - fclose(f); -} - -static sector_t get_dev_blocks(char *dev) -{ - int fd; - sector_t size; - - if ((fd = open(dev, O_RDONLY)) < 0) - err(EXIT_FAILURE, _("cannot open %s"), dev); - if (blkdev_get_sectors(fd, &size) == -1) { - close(fd); - err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev); - } - close(fd); - return size/2; -} - -enum { - ACT_FDISK = 0, /* default */ - ACT_LIST, - ACT_SHOWSIZE -}; - -int main(int argc, char **argv) -{ - int i, c, act = ACT_FDISK; - int colormode = UL_COLORMODE_UNDEF; - struct fdisk_context *cxt; - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - atexit(close_stdout); - - fdisk_init_debug(0); - cxt = fdisk_new_context(); - if (!cxt) - err(EXIT_FAILURE, _("failed to allocate libfdisk context")); - - fdisk_context_set_ask(cxt, ask_callback, NULL); - - while ((c = getopt(argc, argv, "b:c::C:hH:lL::sS:t:u::vV")) != -1) { - switch (c) { - case 'b': - { - size_t sz = strtou32_or_err(optarg, - _("invalid sector size argument")); - if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096) - usage(stderr); - fdisk_save_user_sector_size(cxt, sz, sz); - break; - } - case 'C': - fdisk_save_user_geometry(cxt, - strtou32_or_err(optarg, - _("invalid cylinders argument")), - 0, 0); - break; - case 'c': - if (optarg) { - /* this setting is independent on the current - * actively used label */ - struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos"); - if (!lb) - err(EXIT_FAILURE, _("not found DOS label driver")); - if (strcmp(optarg, "=dos") == 0) - fdisk_dos_enable_compatible(lb, TRUE); - else if (strcmp(optarg, "=nondos") == 0) - fdisk_dos_enable_compatible(lb, FALSE); - else - usage(stderr); - } - /* use default if no optarg specified */ - break; - case 'H': - fdisk_save_user_geometry(cxt, 0, - strtou32_or_err(optarg, - _("invalid heads argument")), - 0); - break; - case 'S': - fdisk_save_user_geometry(cxt, 0, 0, - strtou32_or_err(optarg, - _("invalid sectors argument"))); - break; - case 'l': - act = ACT_LIST; - break; - case 'L': - if (optarg) - colormode = colormode_or_err(optarg, - _("unsupported color mode")); - break; - case 's': - act = ACT_SHOWSIZE; - break; - case 't': - { - struct fdisk_label *lb = NULL; - - while (fdisk_context_next_label(cxt, &lb) == 0) - fdisk_label_set_disabled(lb, 1); - - lb = fdisk_context_get_label(cxt, optarg); - if (!lb) - errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg); - fdisk_label_set_disabled(lb, 0); - } - case 'u': - if (optarg && *optarg == '=') - optarg++; - if (fdisk_context_set_unit(cxt, optarg) != 0) - usage(stderr); - break; - case 'V': - case 'v': - printf(UTIL_LINUX_VERSION); - return EXIT_SUCCESS; - case 'h': - usage(stdout); - default: - usage(stderr); - } - } - - if (argc-optind != 1 && fdisk_has_user_device_properties(cxt)) - warnx(_("The device properties (sector size and geometry) should" - " be used with one specified device only.")); - - colors_init(colormode, "fdisk"); - - switch (act) { - case ACT_LIST: - fdisk_context_enable_listonly(cxt, 1); - - if (argc > optind) { - int k; - for (k = optind; k < argc; k++) - print_device_pt(cxt, argv[k]); - } else - print_all_devices_pt(cxt); - break; - - case ACT_SHOWSIZE: - /* deprecated */ - if (argc - optind <= 0) - usage(stderr); - - for (i = optind; i < argc; i++) { - if (argc - optind == 1) - printf("%llu\n", get_dev_blocks(argv[i])); - else - printf("%s: %llu\n", argv[i], get_dev_blocks(argv[i])); - } - break; - - case ACT_FDISK: - if (argc-optind != 1) - usage(stderr); - - /* Here starts interactive mode, use fdisk_{warn,info,..} functions */ - color_enable(UL_COLOR_GREEN); - fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING); - color_disable(); - fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n" - "Be careful before using the write command.\n")); - - if (fdisk_context_assign_device(cxt, argv[optind], 0) != 0) - err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); - - fflush(stdout); - - if (!fdisk_dev_has_disklabel(cxt)) { - fdisk_info(cxt, _("Device does not contain a recognized partition table.")); - fdisk_create_disklabel(cxt, NULL); - - } else if (fdisk_is_disklabel(cxt, GPT) && fdisk_gpt_is_hybrid(cxt)) - fdisk_warnx(cxt, _( - "The hybrid GPT detected. You have to sync " - "the hybrid MBR manually (expert command 'M').")); - - while (1) - process_fdisk_menu(&cxt); - } - - fdisk_free_context(cxt); - return EXIT_SUCCESS; -} diff --git a/fdisks/fdisk.h b/fdisks/fdisk.h deleted file mode 100644 index 8d8144a7c..000000000 --- a/fdisks/fdisk.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef UTIL_LINUX_FDISK_H -#define UTIL_LINUX_FDISK_H -/* - fdisk.h -*/ - -#include "c.h" - -/* Let's temporary include private libfdisk header file. The final libfdisk.h - * maybe included when fdisk.c and libfdisk code will be completely spit. - */ -#include "fdiskP.h" -#include "blkdev.h" -#include "colors.h" - -extern int get_user_reply(struct fdisk_context *cxt, - const char *prompt, - char *buf, size_t bufsz); -extern int process_fdisk_menu(struct fdisk_context **cxt); - -extern int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, - void *data __attribute__((__unused__))); - -/* prototypes for fdisk.c */ -extern void dump_firstsector(struct fdisk_context *cxt); -extern void dump_disklabel(struct fdisk_context *cxt); - -extern void list_partition_types(struct fdisk_context *cxt); -extern void list_disk_geometry(struct fdisk_context *cxt); -extern void list_disklabel(struct fdisk_context *cxt); -extern void change_partition_type(struct fdisk_context *cxt); -extern struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt); - -extern void toggle_dos_compatibility_flag(struct fdisk_context *cxt); - -#endif /* UTIL_LINUX_FDISK_H */ diff --git a/fdisks/sfdisk.8 b/fdisks/sfdisk.8 deleted file mode 100644 index 62d0ed095..000000000 --- a/fdisks/sfdisk.8 +++ /dev/null @@ -1,650 +0,0 @@ -.\" Copyright 1995 Andries E. Brouwer (aeb@cwi.nl) -.\" May be distributed under the GNU General Public License -.\" The `DOS 6.x Warning' was taken from the old fdisk.8, which says -.\" -- Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu) -.\" -- May be distributed under the GNU General Public License -.\" The `DRDOS Warning' was taken from a net post by Stephen Tweedie. -.\" -.TH SFDISK 8 "August 2011" "util-linux" "System Administration" -.SH NAME -sfdisk \- partition table manipulator for Linux -.SH SYNOPSIS -.B sfdisk -.RB [ options ] -.I device -.br -.B sfdisk \-s -.RI [ partition ] -.SH DESCRIPTION -.B sfdisk -has four (main) uses: list the size of a partition, list the partitions -on a device, check the partitions on a device, and \- very dangerous \- -repartition a device. - -.B sfdisk -doesn't understand the GUID Partition Table (GPT) format and it is not -designed for large partitions. In these cases use the more advanced GNU -.BR parted (8). - -Note that -.B sfdisk -does not align partitions to block-device I/O limits. This functionality is -provided by -.BR fdisk (8). - -.SS "List sizes" -.BI "sfdisk \-s " partition -gives the size of -.I partition -in blocks. This may be useful in connection with programs like -.BR mkswap (8). -Here -.I partition -is usually something like -.I /dev/hda1 -or -.IR /dev/sdb12 , -but may also be an entire disk, like -.IR /dev/xda . - -.RS -.nf -.if t .ft CW -% sfdisk \-s /dev/hda9 -81599 -.if t .ft R -.fi -.RE - -If the partition argument is omitted, -.B sfdisk -will list the sizes of all block devices, and the total: - -.RS -.nf -.if t .ft CW -% sfdisk \-s -/dev/hda: 208896 -/dev/hdb: 1025136 -/dev/hdc: 1031063 -/dev/sda: 8877895 -/dev/sdb: 1758927 -total: 12901917 blocks -.if t .ft R -.fi -.RE - -.SS "List partitions" -The second type of invocation: -.BI "sfdisk \-l " device -will list the partitions on the specified device. If the -.I device -argument is omitted, the partitions on all block devices are listed. - -.RS -.nf -.if t .ft CW -% sfdisk \-l /dev/hdc - -Disk /dev/hdc: 16 heads, 63 sectors, 2045 cylinders -Units = cylinders of 516096 bytes, blocks of 1024 bytes, counting from 0 - - Device Boot Start End #cyls #blocks Id System -/dev/hdc1 0+ 406 407\- 205096+ 83 Linux native -/dev/hdc2 407 813 407 205128 83 Linux native -/dev/hdc3 814 2044 1231 620424 83 Linux native -/dev/hdc4 0 \- 0 0 0 Empty -.if t .ft R -.fi -.RE - -The trailing \- and + signs indicate that rounding has taken place, -and that the actual value is slightly less or more. To see the -exact values, ask for a listing with sectors as unit (\fB\-u S\fR). - -.SS "Check partitions" -The third type of invocation: -.BI "sfdisk \-V " device -will apply various consistency checks to the partition tables on -.IR device . -It prints `OK' or complains. The \fB\-V\fR option can be used -together with \fB\-l\fR. In a shell script one might use -.BI "sfdisk \-V \-q " device -which only returns a status. - -.SS "Create partitions" -The fourth type of invocation: -.BI "sfdisk " device -will cause -.B sfdisk -to read the specification for the desired partitioning of -.I device -from standard input, and then to change the partition tables -on that block device. Thus it is possible to use -.B sfdisk -from a shell script. When -.B sfdisk -determines that its standard input is a terminal, it will be -conversational; otherwise it will abort on any error. -.LP -BE EXTREMELY CAREFUL - ONE TYPING MISTAKE AND ALL YOUR DATA IS LOST -.LP -As a precaution, one can save the sectors changed by -.BR sfdisk : - -.RS -.nf -.if t .ft CW -% sfdisk /dev/hdd \-O hdd-partition-sectors.save -\&... -.if t .ft R -.fi -.RE - -.LP -Then, if you discover that you did something stupid before anything -else has been written to the block device, it may be possible to recover -the old situation with: - -.RS -.nf -.if t .ft CW -% sfdisk /dev/hdd \-I hdd-partition-sectors.save -.if t .ft R -.fi -.RE - -.LP -(This is not the same as saving the old partition table: -a readable version of the old partition table can be saved -using the \fB\-d\fR option. However, if you create logical partitions, -the sectors describing them are located somewhere on block device, -possibly on sectors that were not part of the partition table -before. Thus, the information the \fB\-O\fR option saves -is not a binary version of the output of \fB\-d\fR.) - -There are many options. - -.SH OPTIONS -.TP -.BR \-v ", " \-\-version -Display version information and exit. -.TP -.BR \-h ", " \-\-help -Display help text and exit. -.TP -.BR \-T ", " \-\-list\-types -Print the recognized types (system Id's). -.TP -.BR \-s ", " \-\-show\-size -List the size of a partition. -.TP -.BR \-g ", " \-\-show\-geometry -List the kernel's idea of the geometry of the indicated block device(s). -.TP -.BR \-G ", " \-\-show\-pt\-geometry -List the geometry of the indicated block devices guessed by looking at -the partition table. -.TP -.BR \-l ", " \-\-list -List the partitions of a device. -.TP -.BR \-d ", " \-\-dump -Dump the partitions of a device in a format that is usable as input -to \fBsfdisk\fR. For example, -.br -.nf -.if t .ft CW - % sfdisk -d /dev/hda > hda.out - % sfdisk /dev/hda < hda.out -.if t .ft R -.fi -will correct the bad last extended partition that the OS/2 -fdisk creates. -.TP -.BR \-V ", " \-\-verify -Test whether partitions seem correct. (See the third invocation type above.) -.TP -.BR \-i ", " \-\-increment -Number cylinders etc. starting from 1 instead of 0. -.TP -.BI \-N " number" -Change only the single partition indicated. For example: -.nf -.if t .ft CW - % sfdisk /dev/hdb \-N5 - ,,,* -.if t .ft R -.fi -will make the fifth partition on /dev/hdb bootable (`active') -and change nothing else. (Probably this fifth partition -is called /dev/hdb5, but you are free to call it something else, -like `/my_equipment/disks/2/5' or so). -.TP -\fB\-A\fR, \fB\-\-activate\fR[=\fIdevice_or_number\fR] -Switch on the bootable flag. -.IP -This option takes an optional argument. When no option argument is given, -the command will list the partitions that have the bootable flag set -for the device specified as command argument. For example: -.IP -.nf -.if t .ft CW - % sfdisk --activate /dev/sda -.fi -.IP -When a device name is given as option argument, the partitions specified -as command argument will have the bootable flag switched on. -Other partitions for the same device will have the bootable flag cleared. -For example, with the following command the partitions 1 and 4 are set -to be bootable, while 2 and 3 are cleared: -.IP -.nf -.if t .ft CW - % sfdisk --activate=/dev/sda 1 4 -.fi -.IP -If only a single partition needs to be activated, then the partition number -must be given as option argument, and the device as command argument. For example: -.IP -.nf -.if t .ft CW - % sfdisk --activate=1 /dev/sda -.fi -.IP -The activate option is turned by default on when the program's invocation name is -.BR activate . -.TP -.BR \-c ", " \-\-id " \fInumber\fR [\fIId\fR]" -If no \fIId\fR argument given: print the partition Id of the indicated -partition. If an \fIId\fR argument is present: change the type (Id) of -the indicated partition to the given value. -This option has two longer forms, \fB\-\-print\-id\fR and \fB\-\-change\-id\fR. -For example: -.br -.nf -.if t .ft CW - % sfdisk --print-id /dev/hdb 5 - 6 - % sfdisk --change-id /dev/hdb 5 83 - OK -.if t .ft R -.fi -first reports that /dev/hdb5 has Id 6, and then changes that into 83. -.TP -.BR \-u ", " \-\-unit " \fIletter\fR" -Interpret the input and show the output in the units specified by -.IR letter . -This \fIletter\fR can be one of S, C, B or M, meaning Sectors, Cylinders, -Blocks and Megabytes, respectively. The default is -cylinders, at least when the geometry is known. -.TP -.BR \-x ", " \-\-show\-extended -Also list non-primary extended partitions on output, -and expect descriptors for them on input. -.TP -.BR \-C ", " \-\-cylinders " \fIcylinders\fR" -Specify the number of cylinders, possibly overriding what the kernel thinks. -.TP -.BR \-H ", " \-\-heads " \fIheads\fR" -Specify the number of heads, possibly overriding what the kernel thinks. -.TP -.BR \-S ", " \-\-sectors " \fIsectors\fR" -Specify the number of sectors, possibly overriding what the kernel thinks. -.TP -.BR \-f ", " \-\-force -Do what I say, even if it is stupid. -.TP -.BR \-q ", " \-\-quiet -Suppress warning messages. -.TP -.BR \-L ", " \-\-Linux -Do not complain about things irrelevant for Linux. -.TP -.BR \-D ", " \-\-DOS -For DOS-compatibility: waste a little space. -(More precisely: if a partition cannot contain sector 0, -e.g. because that is the MBR of the device, or contains -the partition table of an extended partition, then -.B sfdisk -would make it start the next sector. However, when this -option is given it skips to the start of the next track, -wasting for example 33 sectors (in case of 34 sectors/track), -just like certain versions of DOS do.) -Certain Disk Managers and boot loaders (such as OSBS, but not -LILO or the OS/2 Boot Manager) also live in this empty space, -so maybe you want this option if you use one. -.TP -.BR \-E ", " \-\-DOS\-extended -Take the starting sector numbers of "inner" extended partitions -to be relative to the starting cylinder boundary of the outer one -(like some versions of DOS do), rather than relative to the actual -starting sector (like Linux does). -(The fact that there is a difference here means that one should -always let extended partitions start at cylinder boundaries if -DOS and Linux should interpret the partition table in the same way. -Of course one can only know where cylinder boundaries are when -one knows what geometry DOS will use for this block device.) -.TP -.BR \-U ", " "\-\-unhide " \fIdevice\fR -Make various Microsoft partition types unhidden. For full list see types -output. -.IP -.nf -.if t .ft CW - % sfdisk --list-types | grep Hidden -.fi -.IP -Notice that the -.B Hidden NTFS WinRE -(Windows Recovery Environment) does not have non-hidden equivalent. -.TP -.BR \-\-IBM ", " \-\-leave\-last -Certain IBM diagnostic programs assume that they can use the -last cylinder on a device for disk-testing purposes. If you think -you might ever run such programs, use this option to tell -.B sfdisk -that it should not allocate the last cylinder. -Sometimes the last cylinder contains a bad sector table. -.TP -.B \-n -Go through all the motions, but do not actually write to block device. -.TP -.BR \-R ", " \-\-re-read -Only execute the BLKRRPART ioctl (to make the kernel re-read -the partition table). This can be useful for checking in advance -that the final BLKRRPART will be successful, and also when you -changed the partition table `by hand' (e.g., using dd from a backup). -If the kernel complains (`device busy for revalidation (usage = 2)') -then something still uses the device, and you still have to unmount -some file system, or say swapoff to some swap partition. -.TP -.B \-\-no\-reread -When starting a repartitioning of a block device, \fBsfdisk\fR checks that this device -is not mounted, or in use as a swap device, and refuses to continue -if it is. This option suppresses the test. (On the other hand, the \fB\-f\fR -option would force \fBsfdisk\fR to continue even when this test fails.) -.TP -.B \-\-in\-order -Partitions are in order. See also warning section. -.TP -.B \-\-not\-in\-order -Partitions are not in order. See also warning section. -.TP -.B \-\-inside\-outer -All logical partitions are inside outermost extended. See also warning -section and chaining. -.TP -.B \-\-not\-inside\-outer -Some, or none, of the logical partitions are not inside outermost -extended. See also warning section and chaining. -.TP -.B \-\-nested -Caution, see warning section. Every partition is contained in the -surrounding partitions and is disjoint from all others. -.TP -.B \-\-chained -Caution, see warning section. Every data partition is contained in -the surrounding partitions and disjoint from all others, but -extended partitions may lie outside (insofar as allowed by -all_logicals_inside_outermost_extended). -.TP -.B \-\-onesector -Caution, see warning section. All data partitions are mutually -disjoint; extended partitions each use one sector only (except -perhaps for the outermost one). -.TP -.BI \-O " file" -Just before writing the new partition, output the sectors -that are going to be overwritten to -.I file -(where hopefully -.I file -resides on another block device, or on a floppy). -.TP -.BI \-I " file" -After destroying your filesystems with an unfortunate -.B sfdisk -command, you would have been able to restore the old situation -if only you had preserved it using the \fB\-O\fR flag. -.TP -.BR \-1 ", " \-\-one-only -Reserved option that does nothing currently. - -.SH THEORY -Block 0 of a block device (the Master Boot Record) contains among -other things four partition descriptors. The partitions -described here are called -.I primary -partitions. -.LP -A partition descriptor has 6 fields: -.br -.nf -.RS -struct partition { - unsigned char bootable; /* 0 or 0x80 */ - hsc begin_hsc; - unsigned char id; - hsc end_hsc; - unsigned int starting_sector; - unsigned int nr_of_sectors; -} -.RE -.fi -.LP -The two hsc fields indicate head, sector and cylinder of the -begin and the end of the partition. Since each hsc field only -takes 3 bytes, only 24 bits are available, which does not -suffice for big block devices (say > 8GB). In fact, due to the wasteful -representation (that uses a byte for the number of heads, which -is typically 16), problems already start with 0.5GB. -However Linux does not use these fields, and problems can arise -only at boot time, before Linux has been started. For more -details, see the -.B lilo -documentation. -.LP -Each partition has a type, its `Id', and if this type is 5 or f -.IR "" "(`" "extended partition" "')" -the starting sector of the partition -again contains 4 partition descriptors. MSDOS only uses the -first two of these: the first one an actual data partition, -and the second one again an extended partition (or empty). -In this way one gets a chain of extended partitions. -Other operating systems have slightly different conventions. -Linux also accepts type 85 as equivalent to 5 and f - this can be -useful if one wants to have extended partitions under Linux past -the 1024 cylinder boundary, without DOS FDISK hanging. -(If there is no good reason, you should just use 5, which is -understood by other systems.) -.LP -Partitions that are not primary or extended are called -.IR logical . -Often, one cannot boot from logical partitions (because the -process of finding them is more involved than just looking -at the MBR). -Note that of an extended partition only the Id and the start -are used. There are various conventions about what to write -in the other fields. One should not try to use extended partitions -for data storage or swap. - -.SH "INPUT FORMAT" -.B sfdisk -reads lines of the form -.br -.RS - -.RE -where each line fills one partition descriptor. -.LP -Fields are separated by whitespace, or comma or semicolon possibly -followed by whitespace; initial and trailing whitespace is ignored. -Numbers can be octal, decimal or hexadecimal, decimal is default. -When a field is absent or empty, a default value is used. -.LP -The parts can (and probably should) be omitted - -.B sfdisk -computes them from and and the block device geometry -as given by the kernel or specified using the \-H, \-S, \-C flags. -.LP -Bootable is specified as [*|\-], with as default not-bootable. -(The value of this field is irrelevant for Linux - when Linux -runs it has been booted already - but might play a role for -certain boot loaders and for other operating systems. -For example, when there are several primary DOS partitions, -DOS assigns C: to the first among these that is bootable.) -.LP -Id is given in hex, without the 0x prefix, or is [E|S|L|X], where -L (LINUX_NATIVE (83)) is the default, S is LINUX_SWAP (82), E -is EXTENDED_PARTITION (5), and X is LINUX_EXTENDED (85). -.LP -The default value of start is the first nonassigned sector/cylinder/... -.LP -The default value of size is as much as possible (until next -partition or end-of-device). -.LP -However, for the four partitions inside an extended partition, -the defaults are: Linux partition, Extended partition, Empty, Empty. -.LP -But when the \-N option (change a single partition only) is given, -the default for each field is its previous value. -.LP -A '+' can be specified instead of a number for size, which means -as much as possible. This is useful with the \-N option. -.SH EXAMPLE -The command -.RS -.nf -.if t .ft CW -sfdisk /dev/hdc << EOF -0,407 -,407 -; -; -EOF -.if t .ft R -.fi -.RE -will partition /dev/hdc just as indicated above. - -The command -.RS -.nf -.if t .ft CW -sfdisk /dev/hdb << EOF -,3,L -,60,L -,19,S -,,E -,130,L -,130,L -,130,L -,,L -EOF -.if t .ft R -.fi -.RE -will partition /dev/hdb into two Linux partitions of 3 and 60 -cylinders, a swap space of 19 cylinders, and an extended partition -covering the rest. Inside the extended partition there are four -Linux logical partitions, three of 130 cylinders and one -covering the rest. - -With the \-x option, the number of input lines must be a multiple of 4: -you have to list the two empty partitions that you never want -using two blank lines. Without the \-x option, you give one line -for the partitions inside a extended partition, instead of four, -and terminate with end-of-file (^D). -(And -.B sfdisk -will assume that your input line represents the first of four, -that the second one is extended, and the 3rd and 4th are empty.) -.SH "CAUTION WARNINGS" - -The options marked with caution in the manual page are dangerous. -For example not all functionality is completely implemented, -which can be a reason for unexpected results. -.SH "DOS 6.x WARNING" - -The DOS 6.x FORMAT command looks for some information in the first -sector of the data area of the partition, and treats this information -as more reliable than the information in the partition table. DOS -FORMAT expects DOS FDISK to clear the first 512 bytes of the data area -of a partition whenever a size change occurs. DOS FORMAT will look at -this extra information even if the /U flag is given -- we consider -this a bug in DOS FORMAT and DOS FDISK. -.LP -The bottom line is that if you use sfdisk to change the size of a -DOS partition table entry, then you must also use -.B dd -to zero the first 512 bytes of that partition before using DOS FORMAT to -format the partition. For example, if you were using sfdisk to make a DOS -partition table entry for /dev/hda1, then (after exiting sfdisk and -rebooting Linux so that the partition table information is valid) you -would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero -the first 512 bytes of the partition. -.B BE EXTREMELY CAREFUL -if you use the -.B dd -command, since a small typo can make all of the data on your block device useless. - -For best results, you should always use an OS-specific partition table -program. For example, you should make DOS partitions with the DOS FDISK -program and Linux partitions with the Linux sfdisk program. - -.SH "DRDOS WARNINGS" - -Stephen Tweedie reported (930515): `Most reports of superblock -corruption turn out to be due to bad partitioning, with one filesystem -overrunning the start of the next and corrupting its superblock. -I have even had this problem with the supposedly-reliable DRDOS. This -was quite possibly due to DRDOS-6.0's FDISK command. Unless I created -a blank track or cylinder between the DRDOS partition and the -immediately following one, DRDOS would happily stamp all over the -start of the next partition. Mind you, as long as I keep a little -free device space after any DRDOS partition, I don't have any other -problems with the two coexisting on the one drive.' - -A. V. Le Blanc writes in README.efdisk: `Dr. DOS 5.0 and 6.0 has been -reported to have problems cooperating with Linux, and with this version -of efdisk in particular. This efdisk sets the system type -to hexadecimal 81. Dr. DOS seems to confuse -this with hexadecimal 1, a DOS code. If you use Dr. DOS, use the -efdisk command 't' to change the system code of any Linux partitions -to some number less than hexadecimal 80; I suggest 41 and 42 for -the moment.' - -A. V. Le Blanc writes in his README.fdisk: `DR-DOS 5.0 and 6.0 -are reported to have difficulties with partition ID codes of 80 or more. -The Linux `fdisk' used to set the system type -of new partitions to hexadecimal 81. DR-DOS seems to confuse this with -hexadecimal 1, a DOS code. The values 82 for swap and 83 for file -systems should not cause problems with DR-DOS. If they do, you may use -the `fdisk' command `t' to change the system code of any Linux -partitions to some number less than hexadecimal 80; I suggest 42 and 43 -for the moment.' - -In fact, it seems that only 4 bits are significant for the DRDOS FDISK, -so that for example 11 and 21 are listed as DOS 2.0. However, DRDOS -itself seems to use the full byte. I have not been able to reproduce -any corruption with DRDOS or its fdisk. - -.SH BUGS -There are too many options. -.LP -There is no support for non-DOS partition types. - -.\" .SH AUTHOR -.\" A. E. Brouwer (aeb@cwi.nl) -.\" -.SH "SEE ALSO" -.BR cfdisk (8), -.BR fdisk (8), -.BR mkfs (8), -.BR parted (8), -.BR partprobe (8), -.BR kpartx (8) -.SH AVAILABILITY -The sfdisk command is part of the util-linux package and is available from -ftp://ftp.kernel.org/pub/linux/utils/util-linux/. diff --git a/fdisks/sfdisk.c b/fdisks/sfdisk.c deleted file mode 100644 index bd32aa8a5..000000000 --- a/fdisks/sfdisk.c +++ /dev/null @@ -1,3230 +0,0 @@ -/* - * sfdisk version 3.0 - aeb - 950813 - * - * Copyright (C) 1995 Andries E. Brouwer (aeb@cwi.nl) - * - * 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 1 - * or (at your option) any later version. - * - * A.V. Le Blanc (LeBlanc@mcc.ac.uk) wrote Linux fdisk 1992-1994, - * patched by various people (faith@cs.unc.edu, martin@cs.unc.edu, - * leisner@sdsp.mc.xerox.com, esr@snark.thyrsus.com, aeb@cwi.nl) - * 1993-1995, with version numbers (as far as I have seen) 0.93 - 2.0e. - * This program had (head,sector,cylinder) as basic unit, and was - * (therefore) broken in several ways for the use on larger disks - - * for example, my last patch (from 2.0d to 2.0e) was required - * to allow a partition to cross cylinder 8064, and to write an - * extended partition past the 4GB mark. - * - * The current program is a rewrite from scratch, and I started a - * version numbering at 3.0. - * Andries Brouwer, aeb@cwi.nl, 950813 - * - * Well, a good user interface is still lacking. On the other hand, - * many configurations cannot be handled by any other fdisk. - * I changed the name to sfdisk to prevent confusion. - aeb, 970501 - */ - -#include -#include /* atoi, free */ -#include /* varargs */ -#include /* read, write */ -#include /* O_RDWR */ -#include /* ERANGE */ -#include /* strchr(), strrchr() */ -#include -#include -#include -#include -#include -#include - -#include "c.h" -#include "nls.h" -#include "xalloc.h" -#include "blkdev.h" -#include "linux_version.h" -#include "wholedisk.h" -#include "pathnames.h" -#include "canonicalize.h" -#include "rpmatch.h" -#include "closestream.h" -#include "strutils.h" - -struct systypes { - unsigned char type; - char *name; -}; - -static struct systypes i386_sys_types[] = { - #include "pt-mbr-partnames.h" -}; - -static char *partname(char *dev, int pno, int lth); - -/* - * Table of contents: - * A. About seeking - * B. About sectors - * C. About heads, sectors and cylinders - * D. About system Ids - * E. About partitions - * F. The standard input - * G. The command line - * H. Listing the current situation - * I. Writing the new situation - */ -int exit_status = 0; - -int force = 0; /* 1: do what I say, even if it is stupid ... */ -int quiet = 0; /* 1: suppress all warnings */ -/* IA-64 gcc spec file currently does -DLinux... */ -#undef Linux -int Linux = 0; /* 1: suppress warnings irrelevant for Linux */ -int DOS = 0; /* 1: shift extended partitions by #sectors, not 1 */ -int DOS_extended = 0; /* 1: use starting cylinder boundary of extd partn */ -int dump = 0; /* 1: list in a format suitable for later input */ -int verify = 0; /* 1: check that listed partition is reasonable */ -int no_write = 0; /* 1: do not actually write to disk */ -int no_reread = 0; /* 1: skip the BLKRRPART ioctl test at startup */ -int leave_last = 0; /* 1: don't allocate the last cylinder */ -int opt_list = 0; -char *save_sector_file = NULL; -char *restore_sector_file = NULL; - -/* - * A. About seeking - */ - -/* - * sseek: seek to specified sector - return 0 on failure - * - * Note: we use 512-byte sectors here, irrespective of the hardware ss. - */ - -static int -sseek(char *dev, int fd, unsigned long s) { - off_t in, out; - in = ((off_t) s << 9); - - if ((out = lseek(fd, in, SEEK_SET)) != in) { - warn(_("seek error on %s - cannot seek to %lu"), dev, s); - return 0; - } - - if (in != out) { - warnx(_("seek error: wanted 0x%08x%08x, got 0x%08x%08x"), - (unsigned int)(in >> 32), (unsigned int)(in & 0xffffffff), - (unsigned int)(out >> 32), (unsigned int)(out & 0xffffffff)); - return 0; - } - return 1; -} - -/* - * B. About sectors - */ - -/* - * We preserve all sectors read in a chain - some of these will - * have to be modified and written back. - */ -struct sector { - struct sector *next; - unsigned long long sectornumber; - int to_be_written; - char data[512]; -} *sectorhead; - -static void -free_sectors(void) { - struct sector *s; - - while (sectorhead) { - s = sectorhead; - sectorhead = s->next; - free(s); - } -} - -static struct sector * -get_sector(char *dev, int fd, unsigned long long sno) { - struct sector *s; - - for (s = sectorhead; s; s = s->next) - if (s->sectornumber == sno) - return s; - - if (!sseek(dev, fd, sno)) - return 0; - - s = xmalloc(sizeof(struct sector)); - - if (read(fd, s->data, sizeof(s->data)) != sizeof(s->data)) { - warn(_("read error on %s - cannot read sector %llu"), dev, sno); - free(s); - return 0; - } - - s->next = sectorhead; - sectorhead = s; - s->sectornumber = sno; - s->to_be_written = 0; - - return s; -} - -static int -msdos_signature(struct sector *s) { - unsigned char *data = (unsigned char *)s->data; - if (data[510] == 0x55 && data[511] == 0xaa) - return 1; - return 0; -} - -static int -write_sectors(char *dev, int fd) { - struct sector *s; - - for (s = sectorhead; s; s = s->next) - if (s->to_be_written) { - if (!sseek(dev, fd, s->sectornumber)) - return 0; - if (write(fd, s->data, sizeof(s->data)) != sizeof(s->data)) { - warn(_("write error on %s - cannot write sector %llu"), - dev, s->sectornumber); - return 0; - } - s->to_be_written = 0; - } - return 1; -} - -static void -ulong_to_chars(unsigned long u, char *uu) { - int i; - - for (i = 0; i < 4; i++) { - uu[i] = (u & 0xff); - u >>= 8; - } -} - -static unsigned long -chars_to_ulong(unsigned char *uu) { - int i; - unsigned long u = 0; - - for (i = 3; i >= 0; i--) - u = (u << 8) | uu[i]; - return u; -} - -static int -save_sectors(char *dev, int fdin) { - struct sector *s; - char ss[516]; - int fdout = -1; - - fdout = open(save_sector_file, O_WRONLY | O_CREAT, 0444); - if (fdout < 0) { - warn(_("cannot open partition sector save file (%s)"), - save_sector_file); - goto err; - } - - for (s = sectorhead; s; s = s->next) - if (s->to_be_written) { - ulong_to_chars(s->sectornumber, ss); - if (!sseek(dev, fdin, s->sectornumber)) - goto err; - if (read(fdin, ss + 4, 512) != 512) { - warn(_("read error on %s - cannot read sector %llu"), - dev, s->sectornumber); - goto err; - } - if (write(fdout, ss, sizeof(ss)) != sizeof(ss)) { - warn(_("write error on %s"), save_sector_file); - goto err; - } - } - - if (close_fd(fdout) != 0) { - warn(_("write failed: %s"), save_sector_file); - return 0; - } - return 1; - - err: - if (fdout >= 0) - if (close_fd(fdout) != 0) - warn(_("write failed: %s"), save_sector_file); - return 0; -} - -static int reread_disk_partition(char *dev, int fd); - -static int -restore_sectors(char *dev) { - int fdin = -1, fdout = -1; - int ct; - struct stat statbuf; - char *ss0 = NULL, *ss; - unsigned long sno; - - if (stat(restore_sector_file, &statbuf) < 0) { - warn(_("cannot stat partition restore file (%s)"), - restore_sector_file); - goto err; - } - if (statbuf.st_size % 516) { - warnx(_("partition restore file has wrong size - not restoring")); - goto err; - } - - ss0 = xmalloc(statbuf.st_size); - ss = ss0; - - fdin = open(restore_sector_file, O_RDONLY); - if (fdin < 0) { - warn(_("cannot open partition restore file (%s)"), - restore_sector_file); - goto err; - } - if (read(fdin, ss, statbuf.st_size) != statbuf.st_size) { - warn(_("error reading %s"), restore_sector_file); - goto err; - } - - fdout = open(dev, O_WRONLY); - if (fdout < 0) { - warn(_("cannot open device %s for writing"), dev); - goto err; - } - - ct = statbuf.st_size / 516; - while (ct--) { - sno = chars_to_ulong((unsigned char *)ss); - if (!sseek(dev, fdout, sno)) - goto err; - if (write(fdout, ss + 4, 512) != 512) { - warn(_("error writing sector %lu on %s"), sno, dev); - goto err; - } - ss += 516; - } - free(ss0); - ss0 = NULL; - - if (!reread_disk_partition(dev, fdout)) /* closes fdout */ - goto err; - close(fdin); - if (close_fd(fdout) != 0) { - warnx(_("write failed: %s"), dev); - return 0; - } - return 1; - - err: - free(ss0); - if (fdin >= 0) - close(fdin); - if (fdout >= 0) - close(fdout); - - return 0; -} - -/* - * C. About heads, sectors and cylinders - */ - -/* - * defines HDIO_GETGEO and - * struct hd_geometry { - * unsigned char heads; - * unsigned char sectors; - * unsigned short cylinders; - * unsigned long start; - * }; - * - * For large disks g.cylinders is truncated, so we use BLKGETSIZE. - */ - -/* - * We consider several geometries for a disk: - * B - the BIOS geometry, gotten from the kernel via HDIO_GETGEO - * F - the fdisk geometry - * U - the user-specified geometry - * - * 0 means unspecified / unknown - */ -struct geometry { - unsigned long long total_size; /* in sectors */ - unsigned long cylindersize; /* in sectors */ - unsigned long heads, sectors, cylinders; - unsigned long start; -} B, F, U; - -static struct geometry -get_geometry(char *dev, int fd, int silent) { - struct hd_geometry g; - unsigned long cyls; - unsigned long long sectors; - struct geometry R; - -#ifdef HDIO_GETGEO - if (ioctl(fd, HDIO_GETGEO, &g)) -#endif - { - g.heads = g.sectors = g.cylinders = g.start = 0; - if (!silent) - warnx(_("Disk %s: cannot get geometry"), dev); - } - - R.start = g.start; - R.heads = g.heads; - R.sectors = g.sectors; - R.cylindersize = R.heads * R.sectors; - R.cylinders = 0; - R.total_size = 0; - - if (blkdev_get_sectors(fd, §ors) == -1) { - /* maybe an ordinary file */ - struct stat s; - - if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode)) - R.total_size = (s.st_size >> 9); - else if (!silent) - warnx(_("Disk %s: cannot get size"), dev); - } else - R.total_size = sectors; - - if (R.cylindersize && R.total_size) { - sectors /= R.cylindersize; - cyls = sectors; - if (cyls != sectors) - cyls = ~0; - R.cylinders = cyls; - } - - return R; -} - -static void -get_cylindersize(char *dev, int fd, int silent) { - struct geometry R; - - R = get_geometry(dev, fd, silent); - - B.heads = (U.heads ? U.heads : R.heads ? R.heads : 255); - B.sectors = (U.sectors ? U.sectors : R.sectors ? R.sectors : 63); - B.cylinders = (U.cylinders ? U.cylinders : R.cylinders); - - B.cylindersize = B.heads * B.sectors; - B.total_size = R.total_size; - - if (B.cylinders == 0 && B.cylindersize != 0) - B.cylinders = B.total_size / B.cylindersize; - - if (R.start && !force) { - warnx(_("Warning: start=%lu - this looks like a partition rather than\n" - "the entire disk. Using fdisk on it is probably meaningless.\n" - "[Use the --force option if you really want this]"), - R.start); - exit(EXIT_FAILURE); - } -#if 0 - if (R.heads && B.heads != R.heads) - warnx(_("Warning: HDIO_GETGEO says that there are %lu heads"), - R.heads); - if (R.sectors && B.sectors != R.sectors) - warnx(_("Warning: HDIO_GETGEO says that there are %lu sectors"), - R.sectors); - if (R.cylinders && B.cylinders != R.cylinders - && B.cylinders < 65536 && R.cylinders < 65536) - warnx(_("Warning: BLKGETSIZE/HDIO_GETGEO says that there are %lu cylinders"), - R.cylinders); -#endif - - if (B.sectors > 63) - warnx(_("Warning: unlikely number of sectors (%lu) - usually at most 63\n" - "This will give problems with all software that uses C/H/S addressing."), - B.sectors); - if (!silent) - printf(_("\nDisk %s: %lu cylinders, %lu heads, %lu sectors/track\n"), - dev, B.cylinders, B.heads, B.sectors); -} - -typedef struct { - unsigned char h, s, c; -} __attribute__ ((packed)) chs; /* has some c bits in s */ -chs zero_chs = { 0, 0, 0 }; - -typedef struct { - unsigned long h, s, c; -} longchs; -longchs zero_longchs; - -static chs -longchs_to_chs(longchs aa, struct geometry G) { - chs a; - - if (aa.h < 256 && aa.s < 64 && aa.c < 1024) { - a.h = aa.h; - a.s = aa.s | ((aa.c >> 2) & 0xc0); - a.c = (aa.c & 0xff); - } else if (G.heads && G.sectors) { - a.h = G.heads - 1; - a.s = G.sectors | 0xc0; - a.c = 0xff; - } else - a = zero_chs; - return a; -} - -static longchs -chs_to_longchs(chs a) { - longchs aa; - - aa.h = a.h; - aa.s = (a.s & 0x3f); - aa.c = (a.s & 0xc0); - aa.c = (aa.c << 2) + a.c; - return aa; -} - -static longchs -ulong_to_longchs(unsigned long sno, struct geometry G) { - longchs aa; - - if (G.heads && G.sectors && G.cylindersize) { - aa.s = 1 + sno % G.sectors; - aa.h = (sno / G.sectors) % G.heads; - aa.c = sno / G.cylindersize; - return aa; - } else { - return zero_longchs; - } -} - -static chs -ulong_to_chs(unsigned long sno, struct geometry G) { - return longchs_to_chs(ulong_to_longchs(sno, G), G); -} - -#if 0 -static unsigned long -longchs_to_ulong(longchs aa, struct geometry G) { - return (aa.c * G.cylindersize + aa.h * G.sectors + aa.s - 1); -} - -static unsigned long -chs_to_ulong(chs a, struct geometry G) { - return longchs_to_ulong(chs_to_longchs(a), G); -} -#endif - -static int -is_equal_chs(chs a, chs b) { - return (a.h == b.h && a.s == b.s && a.c == b.c); -} - -static int -chs_ok(chs a, char *v, char *w) { - longchs aa = chs_to_longchs(a); - int ret = 1; - - if (is_equal_chs(a, zero_chs)) - return 1; - if (B.heads && aa.h >= B.heads) { - warnx(_("%s of partition %s has impossible value for head: " - "%lu (should be in 0-%lu)"), w, v, aa.h, B.heads - 1); - ret = 0; - } - if (B.sectors && (aa.s == 0 || aa.s > B.sectors)) { - warnx(_("%s of partition %s has impossible value for sector: " - "%lu (should be in 1-%lu)"), w, v, aa.s, B.sectors); - ret = 0; - } - if (B.cylinders && aa.c >= B.cylinders) { - warnx(_("%s of partition %s has impossible value for cylinders: " - "%lu (should be in 0-%lu)"), w, v, aa.c, B.cylinders - 1); - ret = 0; - } - return ret; -} - -/* - * D. About system Ids - */ - -#define EMPTY_PARTITION 0 -#define EXTENDED_PARTITION 5 -#define WIN98_EXTENDED 0x0f -#define DM6_AUX1PARTITION 0x51 -#define DM6_AUX3PARTITION 0x53 -#define DM6_PARTITION 0x54 -#define EZD_PARTITION 0x55 -#define LINUX_SWAP 0x82 -#define LINUX_NATIVE 0x83 -#define LINUX_EXTENDED 0x85 -#define BSD_PARTITION 0xa5 -#define NETBSD_PARTITION 0xa9 - -/* List of partition types */ - -static const char * -sysname(unsigned char type) { - struct systypes *s; - - for (s = i386_sys_types; s->name; s++) - if (s->type == type) - return _(s->name); - return _("Unknown"); -} - -static void -list_types(void) { - struct systypes *s; - - printf(_("Id Name\n\n")); - for (s = i386_sys_types; s->name; s++) - printf("%2x %s\n", s->type, _(s->name)); -} - -static int -is_extended(unsigned char type) { - return (type == EXTENDED_PARTITION - || type == LINUX_EXTENDED || type == WIN98_EXTENDED); -} - -static int -is_bsd(unsigned char type) { - return (type == BSD_PARTITION || type == NETBSD_PARTITION); -} - -/* - * E. About partitions - */ - -/* MS/DOS partition */ - -struct partition { - unsigned char bootable; /* 0 or 0x80 */ - chs begin_chs; - unsigned char sys_type; - chs end_chs; - unsigned int start_sect; /* starting sector counting from 0 */ - unsigned int nr_sects; /* nr of sectors in partition */ -} __attribute__ ((packed)); - -/* Unfortunately, partitions are not aligned, and non-Intel machines - are unhappy with non-aligned integers. So, we need a copy by hand. */ -static int -copy_to_int(unsigned char *cp) { - unsigned int m; - - m = *cp++; - m += (*cp++ << 8); - m += (*cp++ << 16); - m += (*cp++ << 24); - return m; -} - -static void -copy_from_int(int m, char *cp) { - *cp++ = (m & 0xff); - m >>= 8; - *cp++ = (m & 0xff); - m >>= 8; - *cp++ = (m & 0xff); - m >>= 8; - *cp++ = (m & 0xff); -} - -static void -copy_to_part(char *cp, struct partition *p) { - p->bootable = *cp++; - p->begin_chs.h = *cp++; - p->begin_chs.s = *cp++; - p->begin_chs.c = *cp++; - p->sys_type = *cp++; - p->end_chs.h = *cp++; - p->end_chs.s = *cp++; - p->end_chs.c = *cp++; - p->start_sect = copy_to_int((unsigned char *)cp); - p->nr_sects = copy_to_int((unsigned char *)cp + 4); -} - -static void -copy_from_part(struct partition *p, char *cp) { - *cp++ = p->bootable; - *cp++ = p->begin_chs.h; - *cp++ = p->begin_chs.s; - *cp++ = p->begin_chs.c; - *cp++ = p->sys_type; - *cp++ = p->end_chs.h; - *cp++ = p->end_chs.s; - *cp++ = p->end_chs.c; - copy_from_int(p->start_sect, cp); - copy_from_int(p->nr_sects, cp + 4); -} - -/* Roughly speaking, Linux doesn't use any of the above fields except - for partition type, start sector and number of sectors. (However, - see also linux/drivers/scsi/fdomain.c.) - The only way partition type is used (in the kernel) is the comparison - for equality with EXTENDED_PARTITION (and these Disk Manager types). */ - -struct part_desc { - unsigned long long start; - unsigned long long size; - unsigned long long sector, offset; /* disk location of this info */ - struct partition p; - struct part_desc *ep; /* extended partition containing this one */ - int ptype; -#define DOS_TYPE 0 -#define BSD_TYPE 1 -} zero_part_desc; - -static struct part_desc * -outer_extended_partition(struct part_desc *p) { - while (p->ep) - p = p->ep; - return p; -} - -static int -is_parent(struct part_desc *pp, struct part_desc *p) { - while (p) { - if (pp == p) - return 1; - p = p->ep; - } - return 0; -} - -struct disk_desc { - struct part_desc partitions[512]; - int partno; -} oldp, newp; - -/* determine where on the disk this information goes */ -static void -add_sector_and_offset(struct disk_desc *z) { - int pno; - struct part_desc *p; - - for (pno = 0; pno < z->partno; pno++) { - p = &(z->partitions[pno]); - p->offset = 0x1be + (pno % 4) * sizeof(struct partition); - p->sector = (p->ep ? p->ep->start : 0); - } -} - -/* tell the kernel to reread the partition tables */ -static int -reread_ioctl(int fd) { -#ifdef BLKRRPART - if (ioctl(fd, BLKRRPART)) -#else - errno = ENOSYS; -#endif - { - /* perror might change errno */ - int err = errno; - - warn("BLKRRPART"); - - /* 2.6.8 returns EIO for a zero table */ - if (err == EBUSY) - return -1; - } - return 0; -} - -/* reread after writing */ -static int -reread_disk_partition(char *dev, int fd) { - fflush(stdout); - sync(); - - if (is_blkdev(fd)) { - printf(_("Re-reading the partition table ...\n")); - if (reread_ioctl(fd) ) { - warnx(_("The command to re-read the partition table failed.\n" - "Run partprobe(8), kpartx(8) or reboot your system now,\n" - "before using mkfs")); - return 0; - } - } - - if (close_fd(fd) != 0) { - warn(_("Error closing %s"), dev); - return 0; - } - printf("\n"); - - return 1; -} - -/* find Linux name of this partition, assuming that it will have a name */ -static int -index_to_linux(int pno, struct disk_desc *z) { - int i, ct = 1; - struct part_desc *p = &(z->partitions[0]); - for (i = 0; i < pno; i++, p++) - if (i < 4 || (p->size > 0 && !is_extended(p->p.sys_type))) - ct++; - return ct; -} - -static int -linux_to_index(int lpno, struct disk_desc *z) { - int i, ct = 0; - struct part_desc *p = &(z->partitions[0]); - for (i = 0; i < z->partno && ct < lpno; i++, p++) - if ((i < 4 || (p->size > 0 && !is_extended(p->p.sys_type))) - && ++ct == lpno) - return i; - return -1; -} - -static int -asc_to_index(char *pnam, struct disk_desc *z) { - int pnum, pno; - - if (*pnam == '#') { - pno = atoi(pnam + 1); - } else { - pnum = atoi(pnam); - pno = linux_to_index(pnum, z); - } - if (!(pno >= 0 && pno < z->partno)) - errx(EXIT_FAILURE, _("%s: no such partition\n"), pnam); - return pno; -} - -/* - * List partitions - in terms of sectors, blocks or cylinders - */ -#define F_SECTOR 1 -#define F_BLOCK 2 -#define F_CYLINDER 3 -#define F_MEGABYTE 4 - -int default_format = F_MEGABYTE; -int specified_format = 0; -int show_extended = 0; -int one_only = 0; -int one_only_pno; -int increment = 0; - -static void -set_format(char c) { - switch (c) { - default: - warnx(_("unrecognized format - using sectors")); - /* fallthrough */ - case 'S': - specified_format = F_SECTOR; - break; - case 'B': - specified_format = F_BLOCK; - break; - case 'C': - specified_format = F_CYLINDER; - break; - case 'M': - specified_format = F_MEGABYTE; - break; - } -} - -static unsigned long -unitsize(int format) { - default_format = (B.cylindersize ? F_CYLINDER : F_MEGABYTE); - if (!format && !(format = specified_format)) - format = default_format; - - switch (format) { - default: - case F_CYLINDER: - if (B.cylindersize) - return B.cylindersize; - /* fallthrough */ - case F_SECTOR: - return 1; - case F_BLOCK: - return 2; - case F_MEGABYTE: - return 2048; - } -} - -static unsigned long long -get_disksize(int format) { - if (B.total_size && leave_last) - /* don't use last cylinder (--leave-last option) */ - return (B.total_size - B.cylindersize) / unitsize(format); - - return B.total_size / unitsize(format); -} - -static void -out_partition_header(char *dev, int format, struct geometry G) { - if (dump) { - printf("# partition table of %s\n", dev); - printf("unit: sectors\n\n"); - return; - } - - default_format = (G.cylindersize ? F_CYLINDER : F_MEGABYTE); - if (!format && !(format = specified_format)) - format = default_format; - - switch (format) { - default: - warnx(_("unimplemented format - using %s"), - G.cylindersize ? _("cylinders") : _("sectors")); - /* fallthrough */ - case F_CYLINDER: - if (G.cylindersize) { - printf(_("Units: cylinders of %lu bytes, blocks of 1024 bytes" - ", counting from %d\n\n"), G.cylindersize << 9, increment); - printf(_(" Device Boot Start End #cyls #blocks Id System\n")); - break; - } - /* fall through */ - case F_SECTOR: - printf(_("Units: sectors of 512 bytes, counting from %d\n\n"), - increment); - printf(_(" Device Boot Start End #sectors Id System\n")); - break; - case F_BLOCK: - printf(_("Units: blocks of 1024 bytes, counting from %d\n\n"), - increment); - printf(_(" Device Boot Start End #blocks Id System\n")); - break; - case F_MEGABYTE: - printf(_("Units: 1MiB = 1024*1024 bytes, blocks of 1024 bytes" - ", counting from %d\n\n"), increment); - printf(_(" Device Boot Start End MiB #blocks Id System\n")); - break; - } -} - -static void -out_rounddown(int width, unsigned long long n, unsigned long unit, int inc) { - printf("%*llu", width, inc + n / unit); - if (unit != 1) - putchar((n % unit) ? '+' : ' '); - putchar(' '); -} - -static void -out_roundup(int width, unsigned long long n, unsigned long unit, int inc) { - if (n == (unsigned long long)(-1)) - printf("%*s", width, "-"); - else - printf("%*llu", width, inc + n / unit); - if (unit != 1) - putchar(((n + 1) % unit) ? '-' : ' '); - putchar(' '); -} - -static void -out_roundup_size(int width, unsigned long long n, unsigned long unit) { - printf("%*llu", width, (n + unit - 1) / unit); - if (unit != 1) - putchar((n % unit) ? '-' : ' '); - putchar(' '); -} - -static struct geometry -get_fdisk_geometry_one(struct part_desc *p) { - struct geometry G; - chs b = p->p.end_chs; - longchs bb = chs_to_longchs(b); - - memset(&G, 0, sizeof(struct geometry)); - G.heads = bb.h + 1; - G.sectors = bb.s; - G.cylindersize = G.heads * G.sectors; - return G; -} - -static int -get_fdisk_geometry(struct disk_desc *z) { - struct part_desc *p; - int pno, agree; - struct geometry G0, G; - - memset(&G0, 0, sizeof(struct geometry)); - agree = 0; - for (pno = 0; pno < z->partno; pno++) { - p = &(z->partitions[pno]); - if (p->size != 0 && p->p.sys_type != 0) { - G = get_fdisk_geometry_one(p); - if (!G0.heads) { - G0 = G; - agree = 1; - } else if (G.heads != G0.heads || G.sectors != G0.sectors) { - agree = 0; - break; - } - } - } - F = (agree ? G0 : B); - return (F.sectors != B.sectors || F.heads != B.heads); -} - -static void -out_partition(char *dev, int format, struct part_desc *p, - struct disk_desc *z, struct geometry G) { - unsigned long long start, end, size; - int pno, lpno; - - if (!format && !(format = specified_format)) - format = default_format; - - pno = p - &(z->partitions[0]); /* our index */ - lpno = index_to_linux(pno, z); /* name of next one that has a name */ - if (pno == linux_to_index(lpno, z)) /* was that us? */ - printf("%s", partname(dev, lpno, 10)); /* yes */ - else if (show_extended) - printf(" - "); - else - return; - putchar(dump ? ':' : ' '); - - start = p->start; - end = p->start + p->size - 1; - size = p->size; - - if (dump) { - printf(" start=%9llu", start); - printf(", size=%9llu", size); - if (p->ptype == DOS_TYPE) { - printf(", Id=%2x", p->p.sys_type); - if (p->p.bootable == 0x80) - printf(", bootable"); - } - printf("\n"); - return; - } - - if (p->ptype != DOS_TYPE || p->p.bootable == 0) - printf(" "); - else if (p->p.bootable == 0x80) - printf(" * "); - else - printf(" ? "); /* garbage */ - - switch (format) { - case F_CYLINDER: - if (G.cylindersize) { - out_rounddown(6, start, G.cylindersize, increment); - out_roundup(6, end, G.cylindersize, increment); - out_roundup_size(6, size, G.cylindersize); - out_rounddown(9, size, 2, 0); - break; - } - /* fall through */ - default: - case F_SECTOR: - out_rounddown(9, start, 1, increment); - out_roundup(9, end, 1, increment); - out_rounddown(10, size, 1, 0); - break; - case F_BLOCK: -#if 0 - printf("%8lu,%3lu ", - p->sector / 2, ((p->sector & 1) ? 512 : 0) + p->offset); -#endif - out_rounddown(8, start, 2, increment); - out_roundup(8, end, 2, increment); - out_rounddown(9, size, 2, 0); - break; - case F_MEGABYTE: - out_rounddown(5, start, 2048, increment); - out_roundup(5, end, 2048, increment); - out_roundup_size(5, size, 2048); - out_rounddown(9, size, 2, 0); - break; - } - if (p->ptype == DOS_TYPE) { - printf(" %2x %s\n", p->p.sys_type, sysname(p->p.sys_type)); - } else { - printf("\n"); - } - - /* Is chs as we expect? */ - if (!quiet && p->ptype == DOS_TYPE) { - chs a, b; - longchs aa, bb; - a = (size ? ulong_to_chs(start, G) : zero_chs); - b = p->p.begin_chs; - aa = chs_to_longchs(a); - bb = chs_to_longchs(b); - if (a.s && !is_equal_chs(a, b)) - printf(_("\t\tstart: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"), - aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); - a = (size ? ulong_to_chs(end, G) : zero_chs); - b = p->p.end_chs; - aa = chs_to_longchs(a); - bb = chs_to_longchs(b); - if (a.s && !is_equal_chs(a, b)) - printf(_("\t\tend: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)\n"), - aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); - if (G.cylinders && G.cylinders < 1024 && bb.c > G.cylinders) - printf(_("partition ends on cylinder %ld, beyond the end of the disk\n"), - bb.c); - } -} - -static void -out_partitions(char *dev, struct disk_desc *z) { - int pno, format = 0; - - if (z->partno == 0) { - if (!opt_list) - warnx(_("No partitions found")); - } else { - if (get_fdisk_geometry(z) && !dump) { - warnx(_("Warning: The partition table looks like it was made\n" - " for C/H/S=*/%ld/%ld (instead of %ld/%ld/%ld).\n" - "For this listing I'll assume that geometry."), - F.heads, F.sectors, B.cylinders, B.heads, B.sectors); - } - - out_partition_header(dev, format, F); - for (pno = 0; pno < z->partno; pno++) { - out_partition(dev, format, &(z->partitions[pno]), z, F); - if (show_extended && pno % 4 == 3) - printf("\n"); - } - } -} - -static int -disj(struct part_desc *p, struct part_desc *q) { - return ((p->start + p->size <= q->start) - || (is_extended(p->p.sys_type) - && q->start + q->size <= p->start + p->size)); -} - -static char * -pnumber(struct part_desc *p, struct disk_desc *z) { - static char buf[20]; - int this, next; - struct part_desc *p0 = &(z->partitions[0]); - - this = index_to_linux(p - p0, z); - next = index_to_linux(p - p0 + 1, z); - - if (next > this) - sprintf(buf, "%d", this); - else - sprintf(buf, "[%d]", this); - return buf; -} - -static int -partitions_ok(int fd, struct disk_desc *z) { - struct part_desc *partitions = &(z->partitions[0]), *p, *q; - int partno = z->partno; - int sector_size; - -#define PNO(p) pnumber(p, z) - - /* Have at least 4 partitions been defined? */ - if (partno < 4) { - if (!partno) - errx(EXIT_FAILURE, _("no partition table present.")); - else - errx(EXIT_FAILURE, _("strange, only %d partitions defined."), partno); - return 0; - } - - /* Are the partitions of size 0 marked empty? - And do they have start = 0? And bootable = 0? */ - for (p = partitions; p - partitions < partno; p++) - if (p->size == 0) { - if (p->p.sys_type != EMPTY_PARTITION) - warnx(_("Warning: partition %s has size 0 but is not marked Empty"), - PNO(p)); - else if (p->p.bootable != 0) - warnx(_("Warning: partition %s has size 0 and is bootable"), - PNO(p)); - else if (p->p.start_sect != 0) - warnx(_("Warning: partition %s has size 0 and nonzero start"), - PNO(p)); - /* all this is probably harmless, no error return */ - } - - /* Are the logical partitions contained in their extended partitions? */ - for (p = partitions + 4; p < partitions + partno; p++) - if (p->ptype == DOS_TYPE) - if (p->size && !is_extended(p->p.sys_type)) { - q = p->ep; - if (p->start < q->start - || p->start + p->size > q->start + q->size) { - warnx(_("Warning: partition %s is not contained in " - "partition %s"), PNO(p), PNO(q)); - return 0; - } - } - - /* Are the data partitions mutually disjoint? */ - for (p = partitions; p < partitions + partno; p++) - if (p->size && !is_extended(p->p.sys_type)) - for (q = p + 1; q < partitions + partno; q++) - if (q->size && !is_extended(q->p.sys_type)) - if (!((p->start > q->start) ? disj(q, p) : disj(p, q))) { - warnx(_("Warning: partitions %s and %s overlap"), - PNO(p), PNO(q)); - return 0; - } - - /* Are the data partitions and the extended partition - table sectors disjoint? */ - for (p = partitions; p < partitions + partno; p++) - if (p->size && !is_extended(p->p.sys_type)) - for (q = partitions; q < partitions + partno; q++) - if (is_extended(q->p.sys_type)) - if (p->start <= q->start && p->start + p->size > q->start) { - warnx(_("Warning: partition %s contains part of " - "the partition table (sector %llu),\n" - "and will destroy it when filled"), - PNO(p), q->start); - return 0; - } - - /* Do they start past zero and end before end-of-disk? */ - { - unsigned long long ds = get_disksize(F_SECTOR); - for (p = partitions; p < partitions + partno; p++) - if (p->size) { - if (p->start == 0) { - warnx(_("Warning: partition %s starts at sector 0"), - PNO(p)); - return 0; - } - if (p->size && p->start + p->size > ds) { - warnx(_("Warning: partition %s extends past end of disk"), - PNO(p)); - return 0; - } - } - } - - if (blkdev_get_sector_size(fd, §or_size) == -1) - sector_size = DEFAULT_SECTOR_SIZE; - - /* Is the size of partitions less than 2^32 sectors limit? */ - for (p = partitions; p < partitions + partno; p++) - if (p->size > UINT_MAX) { - unsigned long long bytes = p->size * sector_size; - int giga = bytes / 1000000000; - int hectogiga = (giga + 50) / 100; - warnx(_("Warning: partition %s has size %d.%d TB (%llu bytes),\n" - "which is larger than the %llu bytes limit imposed\n" - "by the DOS partition table for %d-byte sectors"), - PNO(p), hectogiga / 10, hectogiga % 10, - bytes, - (unsigned long long) UINT_MAX * sector_size, - sector_size); - return 0; - } - - /* Do the partitions start below the 2^32 sectors limit? */ - for (p = partitions; p < partitions + partno; p++) - if (p->start > UINT_MAX) { - unsigned long long bytes = p->start * sector_size; - int giga = bytes / 1000000000; - int hectogiga = (giga + 50) / 100; - warnx(_("Warning: partition %s starts at sector %llu (%d.%d TB for %d-byte sectors),\n" - "which exceeds the DOS partition table limit of %llu sectors"), - PNO(p), - p->start, - hectogiga / 10, - hectogiga % 10, - sector_size, - (unsigned long long) UINT_MAX); - return 0; - } - - /* At most one chain of DOS extended partitions ? */ - /* It seems that the OS/2 fdisk has the additional requirement - that the extended partition must be the fourth one */ - { - int ect = 0; - for (p = partitions; p < partitions + 4; p++) - if (p->p.sys_type == EXTENDED_PARTITION) - ect++; - if (ect > 1 && !Linux) { - warnx(_("Among the primary partitions, at most one can be extended\n" - " (although this is not a problem under Linux)")); - return 0; - } - } - - /* - * Do all partitions start at a cylinder boundary ? - * (this is not required for Linux) - * The first partition starts after MBR. - * Logical partitions start slightly after the containing extended partn. - */ - if (B.cylindersize && !Linux) { - for (p = partitions; p < partitions + partno; p++) - if (p->size) { - if (p->start % B.cylindersize != 0 - && (!p->ep - || p->start / B.cylindersize != - p->ep->start / B.cylindersize) - && (p->p.start_sect >= B.cylindersize)) { - warnx(_("Warning: partition %s does not start " - "at a cylinder boundary"), PNO(p)); - if (specified_format == F_CYLINDER) - return 0; - } - if ((p->start + p->size) % B.cylindersize) { - warnx(_("Warning: partition %s does not end " - "at a cylinder boundary"), PNO(p)); - if (specified_format == F_CYLINDER) - return 0; - } - } - } - - /* Usually, one can boot only from primary partitions. */ - /* In fact, from a unique one only. */ - /* do not warn about bootable extended partitions - - often LILO is there */ - { - int pno = -1; - for (p = partitions; p < partitions + partno; p++) - if (p->p.bootable) { - if (pno == -1) - pno = p - partitions; - else if (p - partitions < 4) { - warnx(_("Warning: more than one primary partition is marked " - "bootable (active)\n" - "This does not matter for LILO, but the DOS MBR will " - "not boot this disk.")); - break; - } - if (p - partitions >= 4) { - warnx(_("Warning: usually one can boot from primary partitions " - "only\nLILO disregards the `bootable' flag.")); - break; - } - } - if (pno == -1 || pno >= 4) - warnx(_("Warning: no primary partition is marked bootable (active)\n" - "This does not matter for LILO, but the DOS MBR will " - "not boot this disk.")); - } - - /* Is chs as we expect? */ - for (p = partitions; p < partitions + partno; p++) - if (p->ptype == DOS_TYPE) { - chs a, b; - longchs aa, bb; - a = p->size ? ulong_to_chs(p->start, B) : zero_chs; - b = p->p.begin_chs; - aa = chs_to_longchs(a); - bb = chs_to_longchs(b); - if (!Linux && !chs_ok(b, PNO(p), _("start"))) - return 0; - if (a.s && !is_equal_chs(a, b)) - warnx(_("partition %s: start: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)"), - PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); - a = p->size ? ulong_to_chs(p->start + p->size - 1, B) : zero_chs; - b = p->p.end_chs; - aa = chs_to_longchs(a); - bb = chs_to_longchs(b); - if (!Linux && !chs_ok(b, PNO(p), _("end"))) - return 0; - if (a.s && !is_equal_chs(a, b)) - warnx(_("partition %s: end: (c,h,s) expected (%ld,%ld,%ld) found (%ld,%ld,%ld)"), - PNO(p), aa.c, aa.h, aa.s, bb.c, bb.h, bb.s); - if (B.cylinders && B.cylinders < 1024 && bb.c > B.cylinders) - warnx(_("partition %s ends on cylinder %ld, beyond the end of the disk"), - PNO(p), bb.c); - } - - return 1; - -#undef PNO -} - -static void -extended_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) { - char *cp; - struct sector *s; - unsigned long long start, here, next; - int i, moretodo = 1; - struct partition p; - struct part_desc *partitions = &(z->partitions[0]); - size_t pno = z->partno; - - here = start = ep->start; - - if (B.cylindersize && start % B.cylindersize) { - /* This is BAD */ - if (DOS_extended) { - here = start -= (start % B.cylindersize); - warnx(_("Warning: shifted start of the extd partition " - "from %lld to %lld\n" - "(For listing purposes only. " - "Do not change its contents.)"), ep->start, start); - } else { - warnx(_("Warning: extended partition does not start at a " - "cylinder boundary.\n" - "DOS and Linux will interpret the contents differently.")); - } - } - - while (moretodo) { - moretodo = 0; - - if (!(s = get_sector(dev, fd, here))) - break; - - if (!msdos_signature(s)) { - warnx(_("ERROR: sector %llu does not have an msdos signature"), - s->sectornumber); - break; - } - cp = s->data + 0x1be; - - if (pno + 4 >= ARRAY_SIZE(z->partitions)) { - warnx(_("too many partitions - ignoring those past nr (%zu)"), - pno - 1); - break; - } - - next = 0; - - for (i = 0; i < 4; i++, cp += sizeof(struct partition)) { - partitions[pno].sector = here; - partitions[pno].offset = cp - s->data; - partitions[pno].ep = ep; - copy_to_part(cp, &p); - if (is_extended(p.sys_type)) { - partitions[pno].start = start + p.start_sect; - if (next) - warnx(_("tree of partitions?")); - else - next = partitions[pno].start; /* follow `upper' branch */ - moretodo = 1; - } else { - partitions[pno].start = here + p.start_sect; - } - partitions[pno].size = p.nr_sects; - partitions[pno].ptype = DOS_TYPE; - partitions[pno].p = p; - pno++; - } - here = next; - } - - z->partno = pno; -} - -#define BSD_DISKMAGIC (0x82564557UL) -#define BSD_MAXPARTITIONS 16 -#define BSD_FS_UNUSED 0 -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -struct bsd_disklabel { - u32 d_magic; - char d_junk1[4]; - char d_typename[16]; - char d_packname[16]; - char d_junk2[92]; - u32 d_magic2; - char d_junk3[2]; - u16 d_npartitions; /* number of partitions in following */ - char d_junk4[8]; - struct bsd_partition { /* the partition table */ - u32 p_size; /* number of sectors in partition */ - u32 p_offset; /* starting sector */ - u32 p_fsize; /* filesystem basic fragment size */ - u8 p_fstype; /* filesystem type, see below */ - u8 p_frag; /* filesystem fragments per block */ - u16 p_cpg; /* filesystem cylinders per group */ - } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ -}; - -static void -bsd_partition(char *dev, int fd, struct part_desc *ep, struct disk_desc *z) { - struct bsd_disklabel *l; - struct bsd_partition *bp, *bp0; - unsigned long long start = ep->start; - struct sector *s; - struct part_desc *partitions = &(z->partitions[0]); - size_t pno = z->partno; - - if (!(s = get_sector(dev, fd, start + 1))) - return; - l = (struct bsd_disklabel *)(s->data); - if (l->d_magic != BSD_DISKMAGIC || l->d_magic2 != BSD_DISKMAGIC) - return; - - bp = bp0 = &l->d_partitions[0]; - while (bp - bp0 < BSD_MAXPARTITIONS && bp - bp0 < l->d_npartitions) { - if (pno + 1 >= ARRAY_SIZE(z->partitions)) { - warnx(_("too many partitions - ignoring those " - "past nr (%zu)"), pno - 1); - break; - } - if (bp->p_fstype != BSD_FS_UNUSED) { - partitions[pno].start = bp->p_offset; - partitions[pno].size = bp->p_size; - partitions[pno].sector = start + 1; - partitions[pno].offset = (char *)bp - (char *)bp0; - partitions[pno].ep = 0; - partitions[pno].ptype = BSD_TYPE; - pno++; - } - bp++; - } - z->partno = pno; -} - -static int -msdos_partition(char *dev, int fd, unsigned long start, struct disk_desc *z) { - int i; - char *cp; - struct partition pt; - struct sector *s; - struct part_desc *partitions = &(z->partitions[0]); - int pno; - int bsd_later = 1; - unsigned short sig, magic; -#ifdef __linux__ - bsd_later = (get_linux_version() >= KERNEL_VERSION(2, 3, 40)); -#endif - - if (!(s = get_sector(dev, fd, start))) - return 0; - - if (!msdos_signature(s)) - return 0; - - cp = s->data + 0x1be; - copy_to_part(cp, &pt); - - /* If I am not mistaken, recent kernels will hide this from us, - so we will never actually see traces of a Disk Manager */ - if (pt.sys_type == DM6_PARTITION - || pt.sys_type == EZD_PARTITION - || pt.sys_type == DM6_AUX1PARTITION - || pt.sys_type == DM6_AUX3PARTITION) { - warnx(_("detected Disk Manager - unable to handle that")); - return 0; - } - - memcpy(&sig, s->data + 2, sizeof(sig)); - if (sig <= 0x1ae) { - memcpy(&magic, s->data + sig, sizeof(magic)); - if (magic == 0x55aa && (1 & *(unsigned char *)(s->data + sig + 2))) { - warnx(_("DM6 signature found - giving up")); - return 0; - } - } - - for (pno = 0; pno < 4; pno++, cp += sizeof(struct partition)) { - partitions[pno].sector = start; - partitions[pno].offset = cp - s->data; - copy_to_part(cp, &pt); - partitions[pno].start = start + pt.start_sect; - partitions[pno].size = pt.nr_sects; - partitions[pno].ep = 0; - partitions[pno].p = pt; - } - - z->partno = pno; - - for (i = 0; i < 4; i++) { - if (is_extended(partitions[i].p.sys_type)) { - if (!partitions[i].size) { - warnx(_("strange..., an extended partition of size 0?")); - continue; - } - extended_partition(dev, fd, &partitions[i], z); - } - if (!bsd_later && is_bsd(partitions[i].p.sys_type)) { - if (!partitions[i].size) { - warnx(_("strange..., a BSD partition of size 0?")); - continue; - } - bsd_partition(dev, fd, &partitions[i], z); - } - } - - if (bsd_later) { - for (i = 0; i < 4; i++) { - if (is_bsd(partitions[i].p.sys_type)) { - if (!partitions[i].size) { - warnx(_("strange..., a BSD partition of size 0?")); - continue; - } - bsd_partition(dev, fd, &partitions[i], z); - } - } - } - - return 1; -} - -static int -osf_partition(char *dev __attribute__ ((__unused__)), - int fd __attribute__ ((__unused__)), - unsigned long start __attribute__ ((__unused__)), - struct disk_desc *z __attribute__ ((__unused__))) { - return 0; -} - -static int -sun_partition(char *dev __attribute__ ((__unused__)), - int fd __attribute__ ((__unused__)), - unsigned long start __attribute__ ((__unused__)), - struct disk_desc *z __attribute__ ((__unused__))) { - return 0; -} - -static int -amiga_partition(char *dev __attribute__ ((__unused__)), - int fd __attribute__ ((__unused__)), - unsigned long start __attribute__ ((__unused__)), - struct disk_desc *z __attribute__ ((__unused__))) { - return 0; -} - -static void -get_partitions(char *dev, int fd, struct disk_desc *z) { - z->partno = 0; - - if (!msdos_partition(dev, fd, 0, z) - && !osf_partition(dev, fd, 0, z) - && !sun_partition(dev, fd, 0, z) - && !amiga_partition(dev, fd, 0, z)) { - if (!opt_list) - warnx(_(" %s: unrecognized partition table type"), dev); - return; - } -} - -static int -write_partitions(char *dev, int fd, struct disk_desc *z) { - struct sector *s; - struct part_desc *partitions = &(z->partitions[0]), *p; - int pno = z->partno; - - if (no_write) { - warnx(_("-n flag was given: Nothing changed")); - exit(EXIT_SUCCESS); - } - - for (p = partitions; p < partitions + pno; p++) { - s = get_sector(dev, fd, p->sector); - if (!s) - return 0; - s->to_be_written = 1; - if (p->ptype == DOS_TYPE) { - copy_from_part(&(p->p), s->data + p->offset); - s->data[510] = 0x55; - s->data[511] = (unsigned char)0xaa; - } - } - if (save_sector_file) { - if (!save_sectors(dev, fd)) { - errx(EXIT_FAILURE, _("Failed saving the old sectors - aborting\n")); - return 0; - } - } - if (!write_sectors(dev, fd)) { - warnx(_("Failed writing the partition on %s"), dev); - return 0; - } - if (fsync(fd)) { - warn(_("Failed writing the partition on %s"), dev); - return 0; - } - return 1; -} - -/* - * F. The standard input - */ - -/* - * Input format: - * - * Fields are separated by whitespace or comma or semicolon possibly - * followed by whitespace; initial and trailing whitespace is ignored. - * Numbers can be octal, decimal or hexadecimal, decimal is default - * The parts can (and probably should) be omitted. - * Bootable is specified as [*|-], with as default not-bootable. - * Type is given in hex, without the 0x prefix, or is [E|S|L|X], where - * L (LINUX_NATIVE (83)) is the default, S is LINUX_SWAP (82), and E - * is EXTENDED_PARTITION (5), X is LINUX_EXTENDED (85). - * The default value of start is the first nonassigned sector/cylinder/... - * The default value of size is as much as possible (until next - * partition or end-of-disk). - * .: end of chain of extended partitions. - * - * On interactive input an empty line means: all defaults. - * Otherwise empty lines are ignored. - */ - -int eof, eob; - -struct dumpfld { - int fldno; - char *fldname; - int is_bool; -} dumpflds[] = { - { - 0, "start", 0}, { - 1, "size", 0}, { - 2, "Id", 0}, { - 3, "bootable", 1}, { - 4, "bh", 0}, { - 5, "bs", 0}, { - 6, "bc", 0}, { - 7, "eh", 0}, { - 8, "es", 0}, { - 9, "ec", 0} -}; - -/* - * Read a line, split it into fields - * - * (some primitive handwork, but a more elaborate parser seems - * unnecessary) - */ -#define RD_EOF (-1) -#define RD_CMD (-2) - -static int -read_stdin(char **fields, char *line, int fieldssize, int linesize) { - char *lp, *ip; - int c, fno; - - /* boolean true and empty string at start */ - line[0] = '*'; - line[1] = 0; - for (fno = 0; fno < fieldssize; fno++) - fields[fno] = line + 1; - fno = 0; - - /* read a line from stdin */ - lp = fgets(line + 2, linesize - 2, stdin); - if (lp == NULL) { - eof = 1; - return RD_EOF; - } - if (!(lp = strchr(lp, '\n'))) - errx(EXIT_FAILURE, _("long or incomplete input line - quitting")); - *lp = 0; - - /* remove comments, if any */ - if ((lp = strchr(line + 2, '#')) != 0) - *lp = 0; - - /* recognize a few commands - to be expanded */ - if (!strcmp(line + 2, "unit: sectors")) { - specified_format = F_SECTOR; - return RD_CMD; - } - - /* dump style? - then bad input is fatal */ - if ((ip = strchr(line + 2, ':')) != 0) { - struct dumpfld *d; - - nxtfld: - ip++; - while (isspace(*ip)) - ip++; - if (*ip == 0) - return fno; - for (d = dumpflds; (size_t) (d - dumpflds) < ARRAY_SIZE(dumpflds); d++) { - if (!strncmp(ip, d->fldname, strlen(d->fldname))) { - ip += strlen(d->fldname); - while (isspace(*ip)) - ip++; - if (d->is_bool) - fields[d->fldno] = line; - else if (*ip == '=') { - while (isspace(*++ip)) ; - fields[d->fldno] = ip; - while (isalnum(*ip)) /* 0x07FF */ - ip++; - } else - errx(EXIT_FAILURE, _("input error: `=' expected after %s field"), - d->fldname); - if (fno <= d->fldno) - fno = d->fldno + 1; - if (*ip == 0) - return fno; - if (*ip != ',' && *ip != ';') - errx(EXIT_FAILURE, _("input error: unexpected character %c after %s field"), - *ip, d->fldname); - *ip = 0; - goto nxtfld; - } - } - errx(EXIT_FAILURE, _("unrecognized input: %s"), ip); - } - - /* split line into fields */ - lp = ip = line + 2; - fields[fno++] = lp; - while ((c = *ip++) != 0) { - if (!lp[-1] && (c == '\t' || c == ' ')) ; - else if (c == '\t' || c == ' ' || c == ',' || c == ';') { - *lp++ = 0; - if (fno < fieldssize) - fields[fno++] = lp; - continue; - } else - *lp++ = c; - } - - if (lp == fields[fno - 1]) - fno--; - return fno; -} - -/* read a number, use default if absent */ -/* a sign gives an offset from the default */ -static int -get_ul(char *u, unsigned long *up, unsigned long def, int base) { - char *nu; - int sign = 0; - unsigned long val; - - if (*u == '+') { - sign = 1; - u++; - } else if (*u == '-') { - sign = -1; - u++; - } - if (*u) { - errno = 0; - val = strtoul(u, &nu, base); - if (errno == ERANGE) { - warnx(_("number too big")); - return -1; - } - if (*nu) { - warnx(_("trailing junk after number")); - return -1; - } - if (sign == 1) - val = def + val; - else if (sign == -1) - val = def - val; - *up = val; - } else - *up = def; - return 0; -} - - -/* read a number, use default if absent */ -/* a sign gives an offset from the default */ -static int -get_ull(char *u, unsigned long long *up, unsigned long long def, int base) { - char *nu; - int sign = 0; - unsigned long long val; - - if (*u == '+') { - sign = 1; - u++; - } else if (*u == '-') { - sign = -1; - u++; - } - if (*u) { - errno = 0; - val = strtoull(u, &nu, base); - if (errno == ERANGE) { - warnx(_("number too big")); - return -1; - } - if (*nu) { - warnx(_("trailing junk after number")); - return -1; - } - if (sign == 1) - val = def + val; - else if (sign == -1) - val = def - val; - *up = val; - } else - *up = def; - return 0; -} - - -/* There are two common ways to structure extended partitions: - as nested boxes, and as a chain. Sometimes the partitions - must be given in order. Sometimes all logical partitions - must lie inside the outermost extended partition. -NESTED: every partition is contained in the surrounding partitions - and is disjoint from all others. -CHAINED: every data partition is contained in the surrounding partitions - and disjoint from all others, but extended partitions may lie outside - (insofar as allowed by all_logicals_inside_outermost_extended). -ONESECTOR: all data partitions are mutually disjoint; extended partitions - each use one sector only (except perhaps for the outermost one). -*/ -int partitions_in_order = 0; -int all_logicals_inside_outermost_extended = 1; -enum { NESTED, CHAINED, ONESECTOR } boxes = NESTED; - -/* find the default value for - assuming entire units */ -static unsigned long long -first_free(int pno, int is_extended, struct part_desc *ep, int format, - unsigned long long mid, struct disk_desc *z) { - unsigned long long ff, fff; - unsigned long unit = unitsize(format); - struct part_desc *partitions = &(z->partitions[0]), *pp = 0; - - /* if containing ep undefined, look at its container */ - if (ep && ep->p.sys_type == EMPTY_PARTITION) - ep = ep->ep; - - if (ep) { - if (boxes == NESTED || (boxes == CHAINED && !is_extended)) - pp = ep; - else if (all_logicals_inside_outermost_extended) - pp = outer_extended_partition(ep); - } -#if 0 - ff = pp ? (pp->start + unit - 1) / unit : 0; -#else - /* rounding up wastes almost an entire cylinder - round down - and leave it to compute_start_sect() to fix the difference */ - ff = pp ? pp->start / unit : 0; -#endif - /* MBR and 1st sector of an extended partition are never free */ - if (unit == 1) - ff++; - - again: - for (pp = partitions; pp < partitions + pno; pp++) { - if (!is_parent(pp, ep) && pp->size > 0) { - if ((partitions_in_order || pp->start / unit <= ff - || (mid && pp->start / unit <= mid)) - && (fff = (pp->start + pp->size + unit - 1) / unit) > ff) { - ff = fff; - goto again; - } - } - } - - return ff; -} - -/* find the default value for - assuming entire units */ -static unsigned long long -max_length(int pno, int is_extended, struct part_desc *ep, int format, - unsigned long long start, struct disk_desc *z) { - unsigned long long fu; - unsigned long unit = unitsize(format); - struct part_desc *partitions = &(z->partitions[0]), *pp = 0; - - /* if containing ep undefined, look at its container */ - if (ep && ep->p.sys_type == EMPTY_PARTITION) - ep = ep->ep; - - if (ep) { - if (boxes == NESTED || (boxes == CHAINED && !is_extended)) - pp = ep; - else if (all_logicals_inside_outermost_extended) - pp = outer_extended_partition(ep); - } - fu = pp ? (pp->start + pp->size) / unit : get_disksize(format); - - for (pp = partitions; pp < partitions + pno; pp++) - if (!is_parent(pp, ep) && pp->size > 0 - && pp->start / unit >= start && pp->start / unit < fu) - fu = pp->start / unit; - - return (fu > start) ? fu - start : 0; -} - -/* compute starting sector of a partition inside an extended one */ -/* return 0 on failure */ -/* ep is 0 or points to surrounding extended partition */ -static int -compute_start_sect(struct part_desc *p, struct part_desc *ep) { - unsigned long long base; - int inc = (DOS && B.sectors) ? B.sectors : 1; - long long delta; - - if (ep && p->start + p->size >= ep->start + 1) - delta = p->start - ep->start - inc; - else if (p->start == 0 && p->size > 0) - delta = -inc; - else - delta = 0; - - if (delta < 0) { - unsigned long long old_size = p->size; - p->start -= delta; - p->size += delta; - if (is_extended(p->p.sys_type) && boxes == ONESECTOR) - p->size = inc; - else if ((long long) old_size <= -delta) { - warnx(_("no room for partition descriptor")); - return 0; - } - } - base = (!ep ? 0 - : (is_extended(p->p.sys_type) ? - outer_extended_partition(ep) : ep)->start); - p->ep = ep; - if (p->p.sys_type == EMPTY_PARTITION && p->size == 0) { - p->p.start_sect = 0; - p->p.begin_chs = zero_chs; - p->p.end_chs = zero_chs; - } else { - p->p.start_sect = p->start - base; - p->p.begin_chs = ulong_to_chs(p->start, B); - p->p.end_chs = ulong_to_chs(p->start + p->size - 1, B); - } - p->p.nr_sects = p->size; - return 1; -} - -/* build the extended partition surrounding a given logical partition */ -static int -build_surrounding_extended(struct part_desc *p, struct part_desc *ep, - struct disk_desc *z) { - int inc = (DOS && B.sectors) ? B.sectors : 1; - int format = F_SECTOR; - struct part_desc *p0 = &(z->partitions[0]), *eep = ep->ep; - - if (boxes == NESTED) { - ep->start = first_free(ep - p0, 1, eep, format, p->start, z); - ep->size = max_length(ep - p0, 1, eep, format, ep->start, z); - if (ep->start > p->start || ep->start + ep->size < p->start + p->size) { - warnx(_("cannot build surrounding extended partition")); - return 0; - } - } else { - ep->start = p->start; - if (boxes == CHAINED) - ep->size = p->size; - else - ep->size = inc; - } - - ep->p.nr_sects = ep->size; - ep->p.bootable = 0; - ep->p.sys_type = EXTENDED_PARTITION; - if (!compute_start_sect(ep, eep) || !compute_start_sect(p, ep)) { - ep->p.sys_type = EMPTY_PARTITION; - ep->size = 0; - return 0; - } - - return 1; -} - -static int -read_line(int pno, struct part_desc *ep, char *dev, int interactive, - struct disk_desc *z) { - char line[1000]; - char *fields[11]; - int fno, pct = pno % 4; - struct part_desc p, *orig; - unsigned long long ff, ff1, ul, ml, ml1, def; - int format, lpno, is_extd; - - if (eof || eob) - return -1; - - lpno = index_to_linux(pno, z); - - if (interactive) { - if (pct == 0 && (show_extended || pno == 0)) - putchar('\n'); - warnx("%s:", partname(dev, lpno, 10)); - } - - /* read input line - skip blank lines when reading from a file */ - do { - fno = read_stdin(fields, line, ARRAY_SIZE(fields), ARRAY_SIZE(line)); - } while (fno == RD_CMD || (fno == 0 && !interactive)); - if (fno == RD_EOF) { - return -1; - } else if (fno > 10 && *(fields[10]) != 0) { - warnx(_("too many input fields")); - return 0; - } - - if (fno == 1 && !strcmp(fields[0], ".")) { - eob = 1; - return -1; - } - - /* use specified format, but round to cylinders if F_MEGABYTE specified */ - format = 0; - if (B.cylindersize && specified_format == F_MEGABYTE && !Linux) - format = F_CYLINDER; - - orig = (one_only ? &(oldp.partitions[pno]) : 0); - - p = zero_part_desc; - p.ep = ep; - - /* first read the type - we need to know whether it is extended */ - /* stop reading when input blank (defaults) and all is full */ - is_extd = 0; - if (fno == 0) { /* empty line */ - if (orig && is_extended(orig->p.sys_type)) - is_extd = 1; - ff = first_free(pno, is_extd, ep, format, 0, z); - ml = max_length(pno, is_extd, ep, format, ff, z); - if (ml == 0 && is_extd == 0) { - is_extd = 1; - ff = first_free(pno, is_extd, ep, format, 0, z); - ml = max_length(pno, is_extd, ep, format, ff, z); - } - if (ml == 0 && pno >= 4) { - /* no free blocks left - don't read any further */ - warnx(_("No room for more")); - return -1; - } - } - if (fno < 3 || !*(fields[2])) - ul = orig ? orig->p.sys_type : - (is_extd || (pno > 3 && pct == 1 && show_extended)) - ? EXTENDED_PARTITION : LINUX_NATIVE; - else if (!strcmp(fields[2], "L")) - ul = LINUX_NATIVE; - else if (!strcmp(fields[2], "S")) - ul = LINUX_SWAP; - else if (!strcmp(fields[2], "E")) - ul = EXTENDED_PARTITION; - else if (!strcmp(fields[2], "X")) - ul = LINUX_EXTENDED; - else if (get_ull(fields[2], &ul, LINUX_NATIVE, 16)) - return 0; - if (ul > 255) { - warnx(_("Illegal type")); - return 0; - } - p.p.sys_type = ul; - is_extd = is_extended(ul); - - /* find start */ - ff = first_free(pno, is_extd, ep, format, 0, z); - ff1 = ff * unitsize(format); - def = orig ? orig->start : (pno > 4 && pct > 1) ? 0 : ff1; - if (fno < 1 || !*(fields[0])) - p.start = def; - else { - if (get_ull(fields[0], &ul, def / unitsize(0), 0)) - return 0; - p.start = ul * unitsize(0); - p.start -= (p.start % unitsize(format)); - } - - /* find length */ - ml = max_length(pno, is_extd, ep, format, p.start / unitsize(format), z); - ml1 = ml * unitsize(format); - def = orig ? orig->size : (pno > 4 && pct > 1) ? 0 : ml1; - if (fno < 2 || !*(fields[1])) - p.size = def; - else if (!strcmp(fields[1], "+")) - p.size = ml1; - else { - if (get_ull(fields[1], &ul, def / unitsize(0), 0)) - return 0; - p.size = ul * unitsize(0) + unitsize(format) - 1; - p.size -= (p.size % unitsize(format)); - } - if (p.size > ml1) { - warnx(_("Warning: given size (%llu) exceeds max allowable size (%llu)"), - (p.size + unitsize(0) - 1) / unitsize(0), ml1 / unitsize(0)); - if (!force) - return 0; - } - if (p.size == 0 && pno >= 4 && (fno < 2 || !*(fields[1]))) { - warnx(_("Warning: empty partition")); - if (!force) - return 0; - } - p.p.nr_sects = p.size; - - if (p.size == 0 && !orig) { - if (fno < 1 || !*(fields[0])) - p.start = 0; - if (fno < 3 || !*(fields[2])) - p.p.sys_type = EMPTY_PARTITION; - } - - if (p.start < ff1 && p.size > 0) { - warnx(_("Warning: bad partition start (earliest %llu)"), - (ff1 + unitsize(0) - 1) / unitsize(0)); - if (!force) - return 0; - } - - if (fno < 4 || !*(fields[3])) - ul = (orig ? orig->p.bootable : 0); - else if (!strcmp(fields[3], "-")) - ul = 0; - else if (!strcmp(fields[3], "*") || !strcmp(fields[3], "+")) - ul = 0x80; - else { - warnx(_("unrecognized bootable flag - choose - or *")); - return 0; - } - p.p.bootable = ul; - - if (ep && ep->p.sys_type == EMPTY_PARTITION) { - if (!build_surrounding_extended(&p, ep, z)) - return 0; - } else if (!compute_start_sect(&p, ep)) - return 0; - - { - longchs aa = chs_to_longchs(p.p.begin_chs), bb; - - if (fno < 5) { - bb = aa; - } else if (fno < 7) { - warnx(_("partial c,h,s specification?")); - return 0; - } else if (get_ul(fields[4], &bb.c, aa.c, 0) || - get_ul(fields[5], &bb.h, aa.h, 0) || - get_ul(fields[6], &bb.s, aa.s, 0)) - return 0; - p.p.begin_chs = longchs_to_chs(bb, B); - } - { - longchs aa = chs_to_longchs(p.p.end_chs), bb; - - if (fno < 8) { - bb = aa; - } else if (fno < 10) { - warnx(_("partial c,h,s specification?")); - return 0; - } else if (get_ul(fields[7], &bb.c, aa.c, 0) || - get_ul(fields[8], &bb.h, aa.h, 0) || - get_ul(fields[9], &bb.s, aa.s, 0)) - return 0; - p.p.end_chs = longchs_to_chs(bb, B); - } - - if (pno > 3 && p.size && show_extended && p.p.sys_type != EMPTY_PARTITION - && (is_extended(p.p.sys_type) != (pct == 1))) { - warnx(_("Extended partition not where expected")); - if (!force) - return 0; - } - - z->partitions[pno] = p; - if (pno >= z->partno) - z->partno += 4; /* reqd for out_partition() */ - - if (interactive) - out_partition(dev, 0, &(z->partitions[pno]), z, B); - - return 1; -} - -/* ep either points to the extended partition to contain this one, - or to the empty partition that may become extended or is 0 */ -static int -read_partition(char *dev, int interactive, int pno, struct part_desc *ep, - struct disk_desc *z) { - struct part_desc *p = &(z->partitions[pno]); - int i; - - if (one_only) { - *p = oldp.partitions[pno]; - if (one_only_pno != pno) - goto ret; - } else if (!show_extended && pno > 4 && pno % 4) - goto ret; - - while (!(i = read_line(pno, ep, dev, interactive, z))) - if (!interactive) - errx(EXIT_FAILURE, _("bad input")); - if (i < 0) { - p->ep = ep; - return 0; - } - - ret: - p->ep = ep; - if (pno >= z->partno) - z->partno += 4; - return 1; -} - -static void -read_partition_chain(char *dev, int interactive, struct part_desc *ep, - struct disk_desc *z) { - int i; - size_t base; - - eob = 0; - while (1) { - base = z->partno; - if (base + 4 > ARRAY_SIZE(z->partitions)) { - warnx(_("too many partitions")); - break; - } - for (i = 0; i < 4; i++) - if (!read_partition(dev, interactive, base + i, ep, z)) - return; - for (i = 0; i < 4; i++) { - ep = &(z->partitions[base + i]); - if (is_extended(ep->p.sys_type) && ep->size) - break; - } - if (i == 4) { - /* nothing found - maybe an empty partition is going - to be extended */ - if (one_only || show_extended) - break; - ep = &(z->partitions[base + 1]); - if (ep->size || ep->p.sys_type != EMPTY_PARTITION) - break; - } - } -} - -static void -read_input(char *dev, int interactive, struct disk_desc *z) { - size_t i; - struct part_desc *partitions = &(z->partitions[0]), *ep; - - for (i = 0; i < ARRAY_SIZE(z->partitions); i++) - partitions[i] = zero_part_desc; - z->partno = 0; - - if (interactive) - warnx(_("Input in the following format; absent fields get a default value.\n" - " \n" - "Usually you only need to specify and (and perhaps ).")); - eof = 0; - - for (i = 0; i < 4; i++) - read_partition(dev, interactive, i, 0, z); - for (i = 0; i < 4; i++) { - ep = partitions + i; - if (is_extended(ep->p.sys_type) && ep->size) - read_partition_chain(dev, interactive, ep, z); - } - add_sector_and_offset(z); -} - -/* - * G. The command line - */ -static void usage(FILE * out) -{ - fputs(USAGE_HEADER, out); - fprintf(out, - _(" %s [options] [...]\n"), program_invocation_short_name); - - fputs(USAGE_OPTIONS, out); - fputs(_(" -s, --show-size list size of a partition\n" - " -c, --id change or print partition Id\n" - " --change-id change Id\n" - " --print-id print Id\n"), out); - fputs(_(" -l, --list list partitions of each device\n" - " -d, --dump idem, but in a format suitable for later input\n" - " -i, --increment number cylinders etc. from 1 instead of from 0\n" - " -u, --unit units to be used; can be one of\n" - " S (sectors), C (cylinders), B (blocks), or M (MB)\n"), out); - fputs(_(" -1, --one-only reserved option that does nothing currently\n" - " -T, --list-types list the known partition types\n" - " -D, --DOS for DOS-compatibility: waste a little space\n" - " -E, --DOS-extended DOS extended partition compatibility\n" - " -R, --re-read make the kernel reread the partition table\n"), out); - fputs(_(" -N change only the partition with this \n" - " -n do not actually write to disk\n" - " -O save the sectors that will be overwritten to \n" - " -I restore sectors from \n"), out); - fputs(_(" -V, --verify check that the listed partitions are reasonable\n" - " -v, --version display version information and exit\n" - " -h, --help display this help text and exit\n"), out); - - fputs(_("\nDangerous options:\n"), out); - fputs(_(" -f, --force disable all consistency checking\n" - " --no-reread do not check whether the partition is in use\n" - " -q, --quiet suppress warning messages\n" - " -L, --Linux do not complain about things irrelevant for Linux\n"), out); - fputs(_(" -g, --show-geometry print the kernel's idea of the geometry\n" - " -G, --show-pt-geometry print geometry guessed from the partition table\n"), out); - fputs(_(" -A, --activate[=] activate bootable flag\n" - " -U, --unhide[=] set partition unhidden\n" - " -x, --show-extended also list extended partitions in the output,\n" - " or expect descriptors for them in the input\n"), out); - fputs(_(" --leave-last do not allocate the last cylinder\n" - " --IBM same as --leave-last\n"), out); - fputs(_(" --in-order partitions are in order\n" - " --not-in-order partitions are not in order\n" - " --inside-outer all logicals inside outermost extended\n" - " --not-inside-outer not all logicals inside outermost extended\n"), out); - fputs(_(" --nested every partition is disjoint from all others\n" - " --chained like nested, but extended partitions may lie outside\n" - " --onesector partitions are mutually disjoint\n"), out); - - fputs(_("\nOverride the detected geometry using:\n" - " -C, --cylinders set the number of cylinders to use\n" - " -H, --heads set the number of heads to use\n" - " -S, --sectors set the number of sectors to use\n"), out); - - fprintf(out, USAGE_MAN_TAIL("sfdisk(8)")); - exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); -} - -static void -activate_usage(void) { - char *p; - if (!strcmp(program_invocation_short_name, "activate")) - p = " "; - else - p = " --activate="; - fputs(USAGE_HEADER, stderr); - fputs(USAGE_SEPARATOR, stderr); - fprintf(stderr, _(" %s%sdevice list active partitions on device\n"), - program_invocation_short_name, p); - fprintf(stderr, _(" %s%sdevice n1 n2 ... activate partitions n1 ..., inactivate the rest\n"), - program_invocation_short_name, p); - fprintf(stderr, USAGE_MAN_TAIL("sfdisk(8)")); - exit(EXIT_FAILURE); -} - -static const char short_opts[] = "cdfghilnqsu:vx1A::C:DGH:I:LN:O:RS:TU::V"; - -#define PRINT_ID 0400 -#define CHANGE_ID 01000 - -enum { - OPT_NO_REREAD = CHAR_MAX + 1, - OPT_LEAVE_LAST, - OPT_IN_ORDER, - OPT_NOT_IN_ORDER, - OPT_INSIDE_OUTER, - OPT_NOT_INSIDE_OUTER, - OPT_NESTED, - OPT_CHAINED, - OPT_ONESECTOR -}; - -static const struct option long_opts[] = { - { "change-id", no_argument, NULL, 'c' + CHANGE_ID }, - { "print-id", no_argument, NULL, 'c' + PRINT_ID }, - { "id", no_argument, NULL, 'c' }, - { "dump", no_argument, NULL, 'd' }, - { "force", no_argument, NULL, 'f' }, - { "show-geometry", no_argument, NULL, 'g' }, - { "help", no_argument, NULL, 'h' }, - { "increment", no_argument, NULL, 'i' }, - { "list", no_argument, NULL, 'l' }, - { "quiet", no_argument, NULL, 'q' }, - { "show-size", no_argument, NULL, 's' }, - { "unit", required_argument, NULL, 'u' }, - { "version", no_argument, NULL, 'v' }, - { "show-extended", no_argument, NULL, 'x' }, - { "one-only", no_argument, NULL, '1' }, - { "cylinders", required_argument, NULL, 'C' }, - { "heads", required_argument, NULL, 'H' }, - { "sectors", required_argument, NULL, 'S' }, - { "show-pt-geometry", no_argument, NULL, 'G' }, - { "activate", optional_argument, NULL, 'A' }, - { "DOS", no_argument, NULL, 'D' }, - { "DOS-extended", no_argument, NULL, 'E' }, - { "Linux", no_argument, NULL, 'L' }, - { "re-read", no_argument, NULL, 'R' }, - { "list-types", no_argument, NULL, 'T' }, - { "unhide", optional_argument, NULL, 'U' }, - { "no-reread", no_argument, NULL, OPT_NO_REREAD }, - { "IBM", no_argument, NULL, OPT_LEAVE_LAST }, - { "leave-last", no_argument, NULL, OPT_LEAVE_LAST }, -/* dangerous flags - not all completely implemented */ - { "in-order", no_argument, NULL, OPT_IN_ORDER }, - { "not-in-order", no_argument, NULL, OPT_NOT_IN_ORDER }, - { "inside-outer", no_argument, NULL, OPT_INSIDE_OUTER }, - { "not-inside-outer", no_argument, NULL, OPT_NOT_INSIDE_OUTER }, - { "nested", no_argument, NULL, OPT_NESTED }, - { "chained", no_argument, NULL, OPT_CHAINED }, - { "onesector", no_argument, NULL, OPT_ONESECTOR }, - { NULL, 0, NULL, 0 } -}; - -static int is_ide_cdrom_or_tape(char *device) -{ - int fd, ret; - - if ((fd = open(device, O_RDONLY)) < 0) - return 0; - ret = blkdev_is_cdrom(fd); - - close(fd); - return ret; -} - -static char * -nextproc(FILE * procf) { - static char devname[256]; - char line[1024], ptname[128 + 1]; - int ma, mi; - unsigned long long sz; - - if (procf == NULL) - return NULL; - while (fgets(line, sizeof(line), procf) != NULL) { - if (sscanf(line, " %d %d %llu %128[^\n ]", &ma, &mi, &sz, ptname) != 4) - continue; - snprintf(devname, sizeof(devname), "/dev/%s", ptname); - if (!is_whole_disk(devname)) - continue; - return canonicalize_path(devname); - } - - return NULL; -} - -static void do_list(char *dev, int silent); -static void do_size(char *dev, int silent); -static void do_geom(char *dev, int silent); -static void do_pt_geom(char *dev, int silent); -static void do_fdisk(char *dev); -static void do_reread(char *dev); -static void do_change_id(char *dev, char *part, char *id); -static void do_unhide(char **av, int ac, char *arg); -static void do_activate(char **av, int ac, char *arg); - -unsigned long long total_size; - -int -main(int argc, char **argv) { - int c; - char *dev; - int opt_size = 0; - int opt_out_geom = 0; - int opt_out_pt_geom = 0; - int opt_reread = 0; - int activate = 0; - int do_id = 0; - int unhide = 0; - char *activatearg = 0; - char *unhidearg = 0; - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - atexit(close_stdout); - - if (argc < 1) - errx(EXIT_FAILURE, _("no command?")); - if (!strcmp(program_invocation_short_name, "activate")) - activate = 1; /* equivalent to `sfdisk -A' */ - - while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { - switch (c) { - case 'f': - force = 1; - break; /* does not imply quiet */ - case 'g': - opt_out_geom = 1; - break; - case 'G': - opt_out_pt_geom = 1; - break; - case 'i': - increment = 1; - break; - case 'c': - case 'c' + PRINT_ID: - case 'c' + CHANGE_ID: - do_id = c; - break; - case 'd': - dump = 1; /* fall through */ - case 'l': - opt_list = 1; - break; - case 'n': - no_write = 1; - break; - case 'q': - quiet = 1; - break; - case 's': - opt_size = 1; - break; - case 'u': - set_format(*optarg); - break; - case 'v': - printf(UTIL_LINUX_VERSION); - return EXIT_SUCCESS; - case 'h': - usage(stdout); - return EXIT_SUCCESS; - case 'x': - show_extended = 1; - break; - case 'A': - activatearg = optarg; - activate = 1; - break; - case 'C': - U.cylinders = strtoul_or_err(optarg, _("invalid cylinders argument")); - break; - case 'D': - DOS = 1; - break; - case 'E': - DOS_extended = 1; - break; - case 'H': - U.heads = strtoul_or_err(optarg, _("invalid heads argument")); - break; - case 'L': - Linux = 1; - break; - case 'N': - one_only = strtol_or_err(optarg, _("invalid number of partitions argument")); - break; - case 'I': - restore_sector_file = optarg; - break; - case 'O': - save_sector_file = optarg; - break; - case 'R': - opt_reread = 1; - break; - case 'S': - U.sectors = strtoul_or_err(optarg, _("invalid sectors argument")); - break; - case 'T': - list_types(); - return EXIT_SUCCESS; - case 'U': - unhidearg = optarg; - unhide = 1; - break; - case 'V': - verify = 1; - break; - default: - usage(stderr); - break; - - /* dangerous flags */ - case OPT_IN_ORDER: - partitions_in_order = 1; - break; - case OPT_NOT_IN_ORDER: - partitions_in_order = 0; - break; - case OPT_INSIDE_OUTER: - all_logicals_inside_outermost_extended = 1; - break; - case OPT_NOT_INSIDE_OUTER: - all_logicals_inside_outermost_extended = 0; - break; - case OPT_NESTED: - boxes = NESTED; - break; - case OPT_CHAINED: - boxes = CHAINED; - break; - case OPT_ONESECTOR: - boxes = ONESECTOR; - break; - - /* more flags */ - case OPT_NO_REREAD: - no_reread = 1; - break; - case OPT_LEAVE_LAST: - leave_last = 1; - break; - } - } - - if (optind == argc && - (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify)) { - FILE *procf; - - /* try all known devices */ - total_size = 0; - - procf = fopen(_PATH_PROC_PARTITIONS, "r"); - if (!procf) - fprintf(stderr, _("cannot open %s\n"), _PATH_PROC_PARTITIONS); - else { - while ((dev = nextproc(procf)) != NULL) { - if (!is_ide_cdrom_or_tape(dev)) { - if (opt_out_geom) - do_geom(dev, 1); - if (opt_out_pt_geom) - do_pt_geom(dev, 1); - if (opt_size) - do_size(dev, 1); - if (opt_list || verify) - do_list(dev, 1); - } - free(dev); - } - fclose(procf); - } - - if (opt_size) - printf(_("total: %llu blocks\n"), total_size); - - return exit_status; - } - - if (optind == argc) { - if (activate) - activate_usage(); - else - usage(stderr); - } - - if (opt_list || opt_out_geom || opt_out_pt_geom || opt_size || verify) { - while (optind < argc) { - if (opt_out_geom) - do_geom(argv[optind], 0); - if (opt_out_pt_geom) - do_pt_geom(argv[optind], 0); - if (opt_size) - do_size(argv[optind], 0); - if (opt_list || verify) - do_list(argv[optind], 0); - optind++; - } - return exit_status; - } - - if (activate) { - do_activate(argv + optind, argc - optind, activatearg); - return exit_status; - } - if (unhide) { - do_unhide(argv + optind, argc - optind, unhidearg); - return exit_status; - } - if (do_id) { - if ((do_id & PRINT_ID) != 0 && optind != argc - 2) - errx(EXIT_FAILURE, _("usage: sfdisk --print-id device partition-number")); - else if ((do_id & CHANGE_ID) != 0 && optind != argc - 3) - errx(EXIT_FAILURE, _("usage: sfdisk --change-id device partition-number Id")); - else if (optind != argc - 3 && optind != argc - 2) - errx(EXIT_FAILURE, _("usage: sfdisk --id device partition-number [Id]")); - do_change_id(argv[optind], argv[optind + 1], - (optind == argc - 2) ? 0 : argv[optind + 2]); - return exit_status; - } - - if (optind != argc - 1) - errx(EXIT_FAILURE, _("can specify only one device (except with -l or -s)")); - dev = argv[optind]; - - if (opt_reread) - do_reread(dev); - else if (restore_sector_file) - restore_sectors(dev); - else - do_fdisk(dev); - - return exit_status; -} - -/* - * H. Listing the current situation - */ - -static int -my_open(char *dev, int rw, int silent) { - int fd, mode; - - mode = (rw ? O_RDWR : O_RDONLY); - fd = open(dev, mode); - if (fd < 0 && !silent) { - if (rw) - err(EXIT_FAILURE, _("cannot open %s read-write"), dev); - else - err(EXIT_FAILURE, _("cannot open %s for reading"), dev); - } - return fd; -} - -static void -do_list(char *dev, int silent) { - int fd; - struct disk_desc *z; - - fd = my_open(dev, 0, silent); - if (fd < 0) - return; - - z = &oldp; - - free_sectors(); - get_cylindersize(dev, fd, dump ? 1 : opt_list ? 0 : 1); - get_partitions(dev, fd, z); - - if (opt_list) - out_partitions(dev, z); - - if (verify) { - if (partitions_ok(fd, z)) - printf(_("%s: OK"), dev); - else - exit_status = 1; - } - - close(fd); -} - -static void -do_geom(char *dev, int silent) { - int fd; - struct geometry R; - - fd = my_open(dev, 0, silent); - if (fd < 0) - return; - - R = get_geometry(dev, fd, silent); - if (R.cylinders) - printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"), - dev, R.cylinders, R.heads, R.sectors); - - close(fd); -} - -static void -do_pt_geom(char *dev, int silent) { - int fd; - struct disk_desc *z; - struct geometry R; - - fd = my_open(dev, 0, silent); - if (fd < 0) - return; - - z = &oldp; - - free_sectors(); - get_cylindersize(dev, fd, 1); - get_partitions(dev, fd, z); - - R = B; - - if (z->partno != 0 && get_fdisk_geometry(z)) { - R.heads = F.heads; - R.sectors = F.sectors; - R.cylindersize = R.heads * R.sectors; - R.cylinders = (R.cylindersize == 0) ? 0 : R.total_size / R.cylindersize; - } - - if (R.cylinders) - printf(_("%s: %ld cylinders, %ld heads, %ld sectors/track\n"), - dev, R.cylinders, R.heads, R.sectors); - - close(fd); -} - -/* for compatibility with earlier fdisk: provide option -s */ -static void -do_size(char *dev, int silent) { - int fd; - unsigned long long size; - - fd = my_open(dev, 0, silent); - if (fd < 0) - return; - - if (blkdev_get_sectors(fd, &size) == -1) { - if (!silent) - err(EXIT_FAILURE, _("Cannot get size of %s"), dev); - goto done; - } - - size /= 2; /* convert sectors to blocks */ - - /* a CDROM drive without mounted CD yields MAXINT */ - if (silent && size == ((1 << 30) - 1)) - goto done; - - if (silent) - printf("%s: %9llu\n", dev, size); - else - printf("%llu\n", size); - - total_size += size; - -done: - close(fd); -} - -/* - * Activate: usually one wants to have a single primary partition - * to be active. OS/2 fdisk makes non-bootable logical partitions - * active - I don't know what that means to OS/2 Boot Manager. - * - * Call: activate /dev/hda 2 5 7 make these partitions active - * and the remaining ones inactive - * Or: sfdisk -A /dev/hda 2 5 7 - * - * If only a single partition must be active, one may also use the form - * sfdisk -A2 /dev/hda - * - * With "activate /dev/hda" or "sfdisk -A /dev/hda" the active partitions - * are listed but not changed. To get zero active partitions, use - * "activate /dev/hda none" or "sfdisk -A /dev/hda none". - * Use something like `echo ",,,*" | sfdisk -N2 /dev/hda' to only make - * /dev/hda2 active, without changing other partitions. - * - * A warning will be given if after the change not precisely one primary - * partition is active. - * - * The present syntax was chosen to be (somewhat) compatible with the - * activate from the LILO package. - */ -static void -set_active(struct disk_desc *z, char *pnam) { - int pno; - - pno = asc_to_index(pnam, z); - if (z->partitions[pno].ptype == DOS_TYPE) - z->partitions[pno].p.bootable = 0x80; -} - -static void -do_activate(char **av, int ac, char *arg) { - char *dev = av[0]; - int fd; - int rw, i, pno, lpno; - struct disk_desc *z; - - z = &oldp; - - rw = (!no_write && (arg || ac > 1)); - fd = my_open(dev, rw, 0); - - free_sectors(); - get_cylindersize(dev, fd, 1); - get_partitions(dev, fd, z); - - if (!arg && ac == 1) { - /* list active partitions */ - for (pno = 0; pno < z->partno; pno++) { - if (z->partitions[pno].p.bootable) { - lpno = index_to_linux(pno, z); - if (pno == linux_to_index(lpno, z)) - printf("%s\n", partname(dev, lpno, 0)); - else - printf("%s#%d\n", dev, pno); - if (z->partitions[pno].p.bootable != 0x80) - warnx(_("bad active byte: 0x%x instead of 0x80"), - z->partitions[pno].p.bootable); - } - } - } else { - /* clear `active byte' everywhere */ - for (pno = 0; pno < z->partno; pno++) - if (z->partitions[pno].ptype == DOS_TYPE) - z->partitions[pno].p.bootable = 0; - - /* then set where desired */ - if (ac == 1) - set_active(z, arg); - else - for (i = 1; i < ac; i++) - set_active(z, av[i]); - - /* then write to disk */ - if (write_partitions(dev, fd, z)) - warnx(_("Done")); - else - exit_status = 1; - } - i = 0; - for (pno = 0; pno < z->partno && pno < 4; pno++) - if (z->partitions[pno].p.bootable) - i++; - if (i != 1) - warnx(_("You have %d active primary partitions. This does not matter for LILO,\n" - "but the DOS MBR will only boot a disk with 1 active partition."), - i); - - if (close_fd(fd) != 0) { - warnx(_("write failed")); - exit_status = 1; - } -} - -static void -set_unhidden(struct disk_desc *z, char *pnam) { - int pno; - unsigned char id; - - pno = asc_to_index(pnam, z); - id = z->partitions[pno].p.sys_type; - if (id == 0x11 || id == 0x14 || id == 0x16 || id == 0x17 || - id == 0x17 || id == 0x1b || id == 0x1c || id == 0x1e) - id -= 0x10; - else - errx(EXIT_FAILURE, _("partition %s has id %x and is not hidden"), pnam, id); - z->partitions[pno].p.sys_type = id; -} - -/* - * maybe remove and make part of --change-id - */ -static void -do_unhide(char **av, int ac, char *arg) { - char *dev = av[0]; - int fd, rw, i; - struct disk_desc *z; - - z = &oldp; - - rw = !no_write; - fd = my_open(dev, rw, 0); - - free_sectors(); - get_cylindersize(dev, fd, 1); - get_partitions(dev, fd, z); - - /* unhide where desired */ - if (ac == 1) - set_unhidden(z, arg); - else - for (i = 1; i < ac; i++) - set_unhidden(z, av[i]); - - /* then write to disk */ - if (write_partitions(dev, fd, z)) - warn(_("Done")); - else - exit_status = 1; - - if (close_fd(fd) != 0) { - warn(_("write failed")); - exit_status = 1; - } -} - -static void -do_change_id(char *dev, char *pnam, char *id) { - int fd, rw, pno; - struct disk_desc *z; - unsigned long i; - - z = &oldp; - - rw = !no_write; - fd = my_open(dev, rw, 0); - - free_sectors(); - get_cylindersize(dev, fd, 1); - get_partitions(dev, fd, z); - - pno = asc_to_index(pnam, z); - if (id == 0) { - printf("%x\n", z->partitions[pno].p.sys_type); - goto done; - } - i = strtoul(id, NULL, 16); - if (i > 255) - errx(EXIT_FAILURE, _("Bad Id %lx"), i); - z->partitions[pno].p.sys_type = i; - - if (write_partitions(dev, fd, z)) - warnx(_("Done")); - else - exit_status = 1; - -done: - if (close_fd(fd) != 0) { - warnx(_("write failed")); - exit_status = 1; - } -} - -static void -do_reread(char *dev) { - int fd; - - fd = my_open(dev, 0, 0); - if (reread_ioctl(fd)) { - warnx(_("This disk is currently in use.")); - exit(EXIT_FAILURE); - } - - close(fd); -} - -/* - * I. Writing the new situation - */ - -static void -do_fdisk(char *dev) { - int fd; - char answer[32]; - struct stat statbuf; - int interactive = isatty(0); - struct disk_desc *z; - - if (stat(dev, &statbuf) < 0) - err(EXIT_FAILURE, _("Fatal error: cannot find %s"), dev); - if (!S_ISBLK(statbuf.st_mode)) { - warnx(_("Warning: %s is not a block device"), dev); - no_reread = 1; - } - fd = my_open(dev, !no_write, 0); - - if (!no_write && !no_reread) { - warnx(_("Checking that no-one is using this disk right now ...")); - if (reread_ioctl(fd)) { - warnx(_("\nThis disk is currently in use - repartitioning is probably a bad idea.\n" - "Umount all file systems, and swapoff all swap partitions on this disk.\n" - "Use the --no-reread flag to suppress this check.")); - if (!force) - errx(EXIT_FAILURE, _("Use the --force flag to overrule all checks.")); - } else - warnx(_("OK")); - } - - z = &oldp; - - free_sectors(); - get_cylindersize(dev, fd, 0); - get_partitions(dev, fd, z); - - printf(_("Old situation:\n")); - out_partitions(dev, z); - - if (one_only && (one_only_pno = linux_to_index(one_only, z)) < 0) - errx(EXIT_FAILURE, _("Partition %d does not exist, cannot change it"), one_only); - - z = &newp; - - while (1) { - - read_input(dev, interactive, z); - - printf(_("New situation:\n")); - out_partitions(dev, z); - - if (!partitions_ok(fd, z) && !force) { - if (!interactive) - errx(EXIT_FAILURE, _("I don't like these partitions - nothing changed.\n" - "(If you really want this, use the --force option.)")); - else - warnx(_("I don't like this - probably you should answer No")); - } - if (interactive) { - ask: - if (no_write) - /* TRANSLATORS: sfdisk uses rpmatch which means the answers y and n - * should be translated, but that is not the case with q answer. */ - printf(_("Are you satisfied with this? [ynq] ")); - else - printf(_("Do you want to write this to disk? [ynq] ")); - ignore_result( fgets(answer, sizeof(answer), stdin) ); - if (answer[0] == 'q' || answer[0] == 'Q') { - errx(EXIT_FAILURE, _("Quitting - nothing changed")); - } else if (rpmatch(answer) == 0) { - continue; - } else if (rpmatch(answer) == 1) { - break; - } else { - printf(_("Please answer one of y,n,q\n")); - goto ask; - } - } else - break; - } - - if (write_partitions(dev, fd, z)) - printf(_("Successfully wrote the new partition table\n\n")); - else - exit_status = 1; - - if (!reread_disk_partition(dev, fd)) { /* close fd on success */ - close(fd); - exit_status = 1; - } - warnx(_("If you created or changed a DOS partition, /dev/foo7, say, then use dd(1)\n" - "to zero the first 512 bytes: dd if=/dev/zero of=/dev/foo7 bs=512 count=1\n" - "(See fdisk(8).)")); - - sync(); /* superstition */ -} - - -/* - * return partition name - uses static storage unless buf is supplied - */ -static char *partname(char *dev, int pno, int lth) -{ - static char bufp[PATH_MAX]; - char *p; - int w, wp; - - w = strlen(dev); - p = ""; - - if (isdigit(dev[w-1])) - p = "p"; - - /* devfs kludge - note: fdisk partition names are not supposed - to equal kernel names, so there is no reason to do this */ - if (strcmp (dev + w - 4, "disc") == 0) { - w -= 4; - p = "part"; - } - - /* udev names partitions by appending -partN - e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 */ - if ((strncmp(dev, _PATH_DEV_BYID, strlen(_PATH_DEV_BYID)) == 0) || - strncmp(dev, _PATH_DEV_BYPATH, strlen(_PATH_DEV_BYPATH)) == 0) { - p = "-part"; - } - - wp = strlen(p); - - if (lth) { - snprintf(bufp, sizeof(bufp), "%*.*s%s%-2u", - lth-wp-2, w, dev, p, pno); - } else { - snprintf(bufp, sizeof(bufp), "%.*s%s%-2u", w, dev, p, pno); - } - return bufp; -} - -- cgit v1.2.3-55-g7522