summaryrefslogtreecommitdiffstats
path: root/disk-utils
diff options
context:
space:
mode:
authorKarel Zak2014-03-10 14:15:36 +0100
committerKarel Zak2014-03-11 11:35:15 +0100
commit4ddd86d5d96f75b8b5191add9b1eaeebb9a548bd (patch)
treeea7c8c5a9be354ec689562028546a8adad2ba19d /disk-utils
parentfdisk: merge fdisk-ask.c to fdisk.c (diff)
downloadkernel-qcow2-util-linux-4ddd86d5d96f75b8b5191add9b1eaeebb9a548bd.tar.gz
kernel-qcow2-util-linux-4ddd86d5d96f75b8b5191add9b1eaeebb9a548bd.tar.xz
kernel-qcow2-util-linux-4ddd86d5d96f75b8b5191add9b1eaeebb9a548bd.zip
build-sys: ove fdisks to disk-utils
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'disk-utils')
-rw-r--r--disk-utils/Makemodule.am73
-rw-r--r--disk-utils/cfdisk.8145
-rw-r--r--disk-utils/cfdisk.c1842
-rw-r--r--disk-utils/fdisk-menu.c880
-rw-r--r--disk-utils/fdisk.8272
-rw-r--r--disk-utils/fdisk.c943
-rw-r--r--disk-utils/fdisk.h36
-rw-r--r--disk-utils/sfdisk.8650
-rw-r--r--disk-utils/sfdisk.c3230
9 files changed, 8071 insertions, 0 deletions
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 <kzak@redhat.com>
+.\"
+.\" 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 <kzak@redhat.com>
+.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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#ifdef HAVE_SLANG_H
+# include <slang.h>
+#elif defined(HAVE_SLANG_SLANG_H)
+# include <slang/slang.h>
+#endif
+
+#ifdef HAVE_SLCURSES_H
+# include <slcurses.h>
+#elif defined(HAVE_SLANG_SLCURSES_H)
+# include <slang/slcurses.h>
+#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
+# include <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+# include <ncurses/ncurses.h>
+#endif
+
+#ifdef HAVE_WIDECHAR
+# include <wctype.h>
+#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 <kzak@redhat.com> "),
+ 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] <disk>\n"), program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -L --color[=<when>] 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#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 <dave@gnu.org>
+.\" Copyright (C) 2013 Karel Zak <kzak@redhat.com>
+.\" 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 +<size>{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 <dave@gnu.org>
+ *
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *
+ * 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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
+
+#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 <linux/compiler.h>
+#endif
+#ifdef HAVE_LINUX_BLKPG_H
+# include <linux/blkpg.h>
+#endif
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %1$s [options] <disk> change partition table\n"
+ " %1$s [options] -l [<disk>] list partition table(s)\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -b <size> sector size (512, 1024, 2048 or 4096)\n"), out);
+ fputs(_(" -c[=<mode>] compatible mode: 'dos' or 'nondos' (default)\n"), out);
+ fputs(_(" -h print this help text\n"), out);
+ fputs(_(" -c[=<mode>] compatible mode: 'dos' or 'nondos' (default)\n"), out);
+ fputs(_(" -L[=<when>] colorize output (auto, always or never)\n"), out);
+ fputs(_(" -t <type> force fdisk to recognize specified partition table type only\n"), out);
+ fputs(_(" -u[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out);
+ fputs(_(" -v print program version\n"), out);
+ fputs(_(" -C <number> specify the number of cylinders\n"), out);
+ fputs(_(" -H <number> specify the number of heads\n"), out);
+ fputs(_(" -S <number> 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 <hex> <name>
+ */
+ 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 <idx> <name> <typestr>
+ */
+ 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
+<start> <size> <id> <bootable> <c,h,s> <c,h,s>
+.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 <c,h,s> parts can (and probably should) be omitted -
+.B sfdisk
+computes them from <start> and <size> 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 <stdio.h>
+#include <stdlib.h> /* atoi, free */
+#include <stdarg.h> /* varargs */
+#include <unistd.h> /* read, write */
+#include <fcntl.h> /* O_RDWR */
+#include <errno.h> /* ERANGE */
+#include <string.h> /* strchr(), strrchr() */
+#include <ctype.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <limits.h>
+
+#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
+ */
+
+/*
+ * <linux/hdreg.h> 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, &sectors) == -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, &sector_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:
+ * <start> <size> <type> <bootable> <c,h,s> <c,h,s>
+ * 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 <c,h,s> 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 <start> - 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 <size> - 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"
+ "<start> <size> <type [E,S,L,X,hex]> <bootable [-,*]> <c,h,s> <c,h,s>\n"
+ "Usually you only need to specify <start> and <size> (and perhaps <type>)."));
+ 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] <device> [...]\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 <letter> units to be used; <letter> 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 <number> change only the partition with this <number>\n"
+ " -n do not actually write to disk\n"
+ " -O <file> save the sectors that will be overwritten to <file>\n"
+ " -I <file> restore sectors from <file>\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[=<device>] activate bootable flag\n"
+ " -U, --unhide[=<dev>] 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 <number> set the number of cylinders to use\n"
+ " -H, --heads <number> set the number of heads to use\n"
+ " -S, --sectors <number> 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;
+}
+