(root)/
util-linux-2.39/
disk-utils/
fdisk-menu.c
       1  
       2  #include <stdio.h>
       3  #include <stdlib.h>
       4  #include <string.h>
       5  #include <ctype.h>
       6  #include <stdint.h>
       7  
       8  #include "c.h"
       9  #include "rpmatch.h"
      10  #include "fdisk.h"
      11  #include "pt-sun.h"
      12  #include "pt-mbr.h"
      13  
      14  struct menu_entry {
      15  	const char	key;			/* command key */
      16  	const char	*title;			/* help string */
      17  	unsigned int	normal : 1,		/* normal mode */
      18  			expert : 1,		/* expert mode */
      19  			hidden : 1;		/* be sensitive for this key,
      20  						   but don't print it in help */
      21  
      22  	enum fdisk_labeltype	label;		/* only for this label */
      23  	int                     exclude;    /* all labels except these */
      24  	enum fdisk_labeltype	parent;		/* for nested PT */
      25  };
      26  
      27  #define IS_MENU_SEP(e)	((e)->key == '-')
      28  #define IS_MENU_HID(e)	((e)->hidden)
      29  
      30  struct menu {
      31  	enum fdisk_labeltype	label;		/* only for this label */
      32  	int                     exclude;    /* all labels except these */
      33  
      34  	unsigned int		nonested : 1;	/* don't make this menu active in nested PT */
      35  
      36  	int (*callback)(struct fdisk_context **,
      37  			const struct menu *,
      38  			const struct menu_entry *);
      39  
      40  	struct menu_entry	entries[];	/* NULL terminated array */
      41  };
      42  
      43  struct menu_context {
      44  	size_t		menu_idx;		/* the current menu */
      45  	size_t		entry_idx;		/* index with in the current menu */
      46  };
      47  
      48  #define MENU_CXT_EMPTY	{ 0, 0 }
      49  #define DECLARE_MENU_CB(x) \
      50  	static int x(struct fdisk_context **, \
      51  		     const struct menu *, \
      52  		     const struct menu_entry *)
      53  
      54  DECLARE_MENU_CB(gpt_menu_cb);
      55  DECLARE_MENU_CB(sun_menu_cb);
      56  DECLARE_MENU_CB(sgi_menu_cb);
      57  DECLARE_MENU_CB(geo_menu_cb);
      58  DECLARE_MENU_CB(dos_menu_cb);
      59  DECLARE_MENU_CB(bsd_menu_cb);
      60  DECLARE_MENU_CB(createlabel_menu_cb);
      61  DECLARE_MENU_CB(generic_menu_cb);
      62  
      63  /*
      64   * Menu entry macros:
      65   *	MENU_X*    expert mode only
      66   *      MENU_B*    both -- expert + normal mode
      67   *
      68   *      *_E  exclude this label
      69   *      *_H  hidden
      70   *      *_L  only for this label
      71   */
      72  
      73  /* separator */
      74  #define MENU_SEP(t)		{ .title = t, .key = '-', .normal = 1 }
      75  #define MENU_XSEP(t)		{ .title = t, .key = '-', .expert = 1 }
      76  #define MENU_BSEP(t)		{ .title = t, .key = '-', .expert = 1, .normal = 1 }
      77  
      78  /* entry */
      79  #define MENU_ENT(k, t)		{ .title = t, .key = k, .normal = 1 }
      80  #define MENU_ENT_E(k, t, l)	{ .title = t, .key = k, .normal = 1, .exclude = l }
      81  #define MENU_ENT_L(k, t, l)	{ .title = t, .key = k, .normal = 1, .label = l }
      82  
      83  #define MENU_XENT(k, t)		{ .title = t, .key = k, .expert = 1 }
      84  #define MENU_XENT_H(k, t)	{ .title = t, .key = k, .expert = 1, .hidden = 1 }
      85  
      86  #define MENU_BENT(k, t)		{ .title = t, .key = k, .expert = 1, .normal = 1 }
      87  #define MENU_BENT_E(k, t, l)	{ .title = t, .key = k, .expert = 1, .normal = 1, .exclude = l }
      88  
      89  #define MENU_ENT_NEST(k, t, l, p)	{ .title = t, .key = k, .normal = 1, .label = l, .parent = p }
      90  #define MENU_BENT_NEST_H(k, t, l, p)	{ .title = t, .key = k, .expert = 1, .normal = 1, .label = l, .parent = p, .hidden = 1 }
      91  
      92  /* Generic menu */
      93  static const struct menu menu_generic = {
      94  	.callback	= generic_menu_cb,
      95  	.entries	= {
      96  		MENU_BSEP(N_("Generic")),
      97  		MENU_ENT  ('d', N_("delete a partition")),
      98  		MENU_ENT  ('F', N_("list free unpartitioned space")),
      99  		MENU_ENT  ('l', N_("list known partition types")),
     100  		MENU_ENT  ('n', N_("add a new partition")),
     101  		MENU_BENT ('p', N_("print the partition table")),
     102  		MENU_ENT  ('t', N_("change a partition type")),
     103  		MENU_BENT_E('v', N_("verify the partition table"), FDISK_DISKLABEL_BSD),
     104  		MENU_ENT  ('i', N_("print information about a partition")),
     105  
     106  		MENU_XENT('d', N_("print the raw data of the first sector from the device")),
     107  		MENU_XENT('D', N_("print the raw data of the disklabel from the device")),
     108  		MENU_XENT('f', N_("fix partitions order")),
     109  
     110  		MENU_SEP(N_("Misc")),
     111  		MENU_BENT ('m', N_("print this menu")),
     112  		MENU_ENT_E('u', N_("change display/entry units"), FDISK_DISKLABEL_GPT),
     113  		MENU_ENT_E('x', N_("extra functionality (experts only)"), FDISK_DISKLABEL_BSD),
     114  
     115  		MENU_SEP(N_("Script")),
     116  		MENU_ENT  ('I', N_("load disk layout from sfdisk script file")),
     117  		MENU_ENT  ('O', N_("dump disk layout to sfdisk script file")),
     118  
     119  		MENU_BSEP(N_("Save & Exit")),
     120  		MENU_ENT_E('w', N_("write table to disk and exit"), FDISK_DISKLABEL_BSD),
     121  		MENU_ENT_L('w', N_("write table to disk"), FDISK_DISKLABEL_BSD),
     122  		MENU_BENT ('q', N_("quit without saving changes")),
     123  		MENU_XENT ('r', N_("return to main menu")),
     124  
     125  		MENU_ENT_NEST('r', N_("return from BSD to DOS (MBR)"), FDISK_DISKLABEL_BSD, FDISK_DISKLABEL_DOS),
     126  
     127  		MENU_ENT_NEST('r', N_("return from protective/hybrid MBR to GPT"), FDISK_DISKLABEL_DOS, FDISK_DISKLABEL_GPT),
     128  
     129  		{ 0, NULL }
     130  	}
     131  };
     132  
     133  static const struct menu menu_createlabel = {
     134  	.callback = createlabel_menu_cb,
     135  	.exclude = FDISK_DISKLABEL_BSD,
     136  	.nonested = 1,
     137  	.entries = {
     138  		MENU_SEP(N_("Create a new label")),
     139  		MENU_ENT('g', N_("create a new empty GPT partition table")),
     140  		MENU_ENT('G', N_("create a new empty SGI (IRIX) partition table")),
     141  		MENU_ENT('o', N_("create a new empty MBR (DOS) partition table")),
     142  		MENU_ENT('s', N_("create a new empty Sun partition table")),
     143  
     144  		/* backward compatibility -- be sensitive to 'g', but don't
     145  		 * print it in the expert menu */
     146  		MENU_XENT_H('g', N_("create an IRIX (SGI) partition table")),
     147  		{ 0, NULL }
     148  	}
     149  };
     150  
     151  static const struct menu menu_geo = {
     152  	.callback = geo_menu_cb,
     153  	.exclude = FDISK_DISKLABEL_GPT | FDISK_DISKLABEL_BSD,
     154  	.entries = {
     155  		MENU_XSEP(N_("Geometry (for the current label)")),
     156  		MENU_XENT('c', N_("change number of cylinders")),
     157  		MENU_XENT('h', N_("change number of heads")),
     158  		MENU_XENT('s', N_("change number of sectors/track")),
     159  		{ 0, NULL }
     160  	}
     161  };
     162  
     163  static const struct menu menu_gpt = {
     164  	.callback = gpt_menu_cb,
     165  	.label = FDISK_DISKLABEL_GPT,
     166  	.entries = {
     167  		MENU_BSEP(N_("GPT")),
     168  		MENU_XENT('i', N_("change disk GUID")),
     169  		MENU_XENT('n', N_("change partition name")),
     170  		MENU_XENT('u', N_("change partition UUID")),
     171  		MENU_XENT('l', N_("change table length")),
     172  		MENU_BENT('M', N_("enter protective/hybrid MBR")),
     173  
     174  		MENU_XSEP(""),
     175  		MENU_XENT('A', N_("toggle the legacy BIOS bootable flag")),
     176  		MENU_XENT('B', N_("toggle the no block IO protocol flag")),
     177  		MENU_XENT('R', N_("toggle the required partition flag")),
     178  		MENU_XENT('S', N_("toggle the GUID specific bits")),
     179  
     180  		{ 0, NULL }
     181  	}
     182  };
     183  
     184  static const struct menu menu_sun = {
     185  	.callback = sun_menu_cb,
     186  	.label = FDISK_DISKLABEL_SUN,
     187  	.entries = {
     188  		MENU_BSEP(N_("Sun")),
     189  		MENU_ENT('a', N_("toggle the read-only flag")),
     190  		MENU_ENT('c', N_("toggle the mountable flag")),
     191  
     192  		MENU_XENT('a', N_("change number of alternate cylinders")),
     193  		MENU_XENT('e', N_("change number of extra sectors per cylinder")),
     194  		MENU_XENT('i', N_("change interleave factor")),
     195  		MENU_XENT('o', N_("change rotation speed (rpm)")),
     196  		MENU_XENT('y', N_("change number of physical cylinders")),
     197  		{ 0, NULL }
     198  	}
     199  };
     200  
     201  static const struct menu menu_sgi = {
     202  	.callback = sgi_menu_cb,
     203  	.label = FDISK_DISKLABEL_SGI,
     204  	.entries = {
     205  		MENU_SEP(N_("SGI")),
     206  		MENU_ENT('a', N_("select bootable partition")),
     207  		MENU_ENT('b', N_("edit bootfile entry")),
     208  		MENU_ENT('c', N_("select sgi swap partition")),
     209  		MENU_ENT('i', N_("create SGI info")),
     210  		{ 0, NULL }
     211  	}
     212  };
     213  
     214  static const struct menu menu_dos = {
     215  	.callback = dos_menu_cb,
     216  	.label = FDISK_DISKLABEL_DOS,
     217  	.entries = {
     218  		MENU_BSEP(N_("DOS (MBR)")),
     219  		MENU_ENT('a', N_("toggle a bootable flag")),
     220  		MENU_ENT('b', N_("edit nested BSD disklabel")),
     221  		MENU_ENT('c', N_("toggle the dos compatibility flag")),
     222  
     223  		MENU_XENT('b', N_("move beginning of data in a partition")),
     224  		MENU_XENT('F', N_("fix partitions C/H/S values")),
     225  		MENU_XENT('i', N_("change the disk identifier")),
     226  
     227  		MENU_BENT_NEST_H('M', N_("return from protective/hybrid MBR to GPT"), FDISK_DISKLABEL_DOS, FDISK_DISKLABEL_GPT),
     228  
     229  		{ 0, NULL }
     230  	}
     231  };
     232  
     233  static const struct menu menu_bsd = {
     234  	.callback = bsd_menu_cb,
     235  	.label = FDISK_DISKLABEL_BSD,
     236  	.entries = {
     237  		MENU_SEP(N_("BSD")),
     238  		MENU_ENT('e', N_("edit drive data")),
     239  		MENU_ENT('i', N_("install bootstrap")),
     240  		MENU_ENT('s', N_("show complete disklabel")),
     241  		MENU_ENT('x', N_("link BSD partition to non-BSD partition")),
     242  		{ 0, NULL }
     243  	}
     244  };
     245  
     246  static const struct menu *menus[] = {
     247  	&menu_gpt,
     248  	&menu_sun,
     249  	&menu_sgi,
     250  	&menu_dos,
     251  	&menu_bsd,
     252  	&menu_geo,
     253  	&menu_generic,
     254  	&menu_createlabel,
     255  };
     256  
     257  static const struct menu_entry *next_menu_entry(
     258  			struct fdisk_context *cxt,
     259  			struct menu_context *mc)
     260  {
     261  	struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
     262  	struct fdisk_context *parent = fdisk_get_parent(cxt);
     263  	unsigned int type = 0, pr_type = 0;
     264  
     265  	assert(cxt);
     266  
     267  	if (lb)
     268  		type = fdisk_label_get_type(lb);
     269  	if (parent)
     270  		pr_type = fdisk_label_get_type(fdisk_get_label(parent, NULL));
     271  
     272  	while (mc->menu_idx < ARRAY_SIZE(menus)) {
     273  		const struct menu *m = menus[mc->menu_idx];
     274  		const struct menu_entry *e = &(m->entries[mc->entry_idx]);
     275  
     276  		/*
     277  		 * whole-menu filter
     278  		 */
     279  
     280  		/* no more entries */
     281  		if (e->title == NULL ||
     282  		/* menu wanted for specified labels only */
     283  		    (m->label && (!lb || !(m->label & type))) ||
     284  		/* unwanted for nested PT */
     285  		    (m->nonested && parent) ||
     286  		/* menu excluded for specified labels */
     287  		    (m->exclude && lb && (m->exclude & type))) {
     288  			mc->menu_idx++;
     289  			mc->entry_idx = 0;
     290  			continue;
     291  		}
     292  
     293  		/*
     294  		 * per entry filter
     295  		 */
     296  
     297  		/* excluded for the current label */
     298  		if ((e->exclude && lb && e->exclude & type) ||
     299  		/* entry wanted for specified labels only */
     300  		    (e->label && (!lb || !(e->label & type))) ||
     301  		/* exclude non-expert entries in expect mode */
     302  		    (e->expert == 0 && fdisk_is_details(cxt)) ||
     303  		/* nested only */
     304  		    (e->parent && (!parent || pr_type != e->parent)) ||
     305  		/* exclude non-normal entries in normal mode */
     306  		    (e->normal == 0 && !fdisk_is_details(cxt))) {
     307  			mc->entry_idx++;
     308  			continue;
     309  		}
     310  		mc->entry_idx++;
     311  		return e;
     312  
     313  	}
     314  	return NULL;
     315  }
     316  
     317  /* returns @menu and menu entry for then @key */
     318  static const struct menu_entry *get_fdisk_menu_entry(
     319  		struct fdisk_context *cxt,
     320  		int key,
     321  		const struct menu **menu)
     322  {
     323  	struct menu_context mc = MENU_CXT_EMPTY;
     324  	const struct menu_entry *e;
     325  
     326  	while ((e = next_menu_entry(cxt, &mc))) {
     327  		if (IS_MENU_SEP(e) || e->key != key)
     328  			continue;
     329  
     330  		if (menu)
     331  			*menu = menus[mc.menu_idx];
     332  		return e;
     333  	}
     334  
     335  	return NULL;
     336  }
     337  
     338  static int menu_detect_collisions(struct fdisk_context *cxt)
     339  {
     340  	struct menu_context mc = MENU_CXT_EMPTY;
     341  	const struct menu_entry *e, *r;
     342  
     343  	while ((e = next_menu_entry(cxt, &mc))) {
     344  		if (IS_MENU_SEP(e))
     345  			continue;
     346  
     347  		r = get_fdisk_menu_entry(cxt, e->key, NULL);
     348  		if (!r) {
     349  			DBG(MENU, ul_debug("warning: not found "
     350  					"entry for %c", e->key));
     351  			return -1;
     352  		}
     353  		if (r != e) {
     354  			DBG(MENU, ul_debug("warning: duplicate key '%c'",
     355  						e->key));
     356  			DBG(MENU, ul_debug("       : %s", e->title));
     357  			DBG(MENU, ul_debug("       : %s", r->title));
     358  			abort();
     359  		}
     360  	}
     361  
     362  	return 0;
     363  }
     364  
     365  static int print_fdisk_menu(struct fdisk_context *cxt)
     366  {
     367  	struct menu_context mc = MENU_CXT_EMPTY;
     368  	const struct menu_entry *e;
     369  
     370  	ON_DBG(MENU, menu_detect_collisions(cxt));
     371  
     372  	if (fdisk_is_details(cxt))
     373  		printf(_("\nHelp (expert commands):\n"));
     374  	else
     375  		printf(_("\nHelp:\n"));
     376  
     377  	while ((e = next_menu_entry(cxt, &mc))) {
     378  		if (IS_MENU_HID(e))
     379  			continue;	/* hidden entry */
     380  		if (IS_MENU_SEP(e) && (!e->title || !*e->title))
     381  			printf("\n");
     382  		else if (IS_MENU_SEP(e)) {
     383  			color_scheme_enable("help-title", UL_COLOR_BOLD);
     384  			printf("\n  %s\n", _(e->title));
     385  			color_disable();
     386  		} else
     387  			printf("   %c   %s\n", e->key, _(e->title));
     388  	}
     389  	fputc('\n', stdout);
     390  
     391  	if (fdisk_get_parent(cxt)) {
     392  		struct fdisk_label *l = fdisk_get_label(cxt, NULL),
     393  				   *p = fdisk_get_label(fdisk_get_parent(cxt), NULL);
     394  
     395  		fdisk_info(cxt, _("You're editing nested '%s' partition table, "
     396  				  "primary partition table is '%s'."),
     397  				fdisk_label_get_name(l),
     398  				fdisk_label_get_name(p));
     399  	}
     400  
     401  	return 0;
     402  }
     403  
     404  /* Asks for command, verify the key and perform the command or
     405   * returns the command key if no callback for the command is
     406   * implemented.
     407   *
     408   * Note that this function might exchange the context pointer to
     409   * switch to another (nested) context.
     410   *
     411   * Returns: <0 on error
     412   *           0 on success (the command performed)
     413   *          >0 if no callback (then returns the key)
     414   */
     415  int process_fdisk_menu(struct fdisk_context **cxt0)
     416  {
     417  	struct fdisk_context *cxt = *cxt0;
     418  	const struct menu_entry *ent;
     419  	const struct menu *menu;
     420  	int key, rc;
     421  	const char *prompt;
     422  	char buf[BUFSIZ] = { '\0' };
     423  
     424  	if (fdisk_is_details(cxt))
     425  		prompt = _("Expert command (m for help): ");
     426  	else
     427  		prompt = _("Command (m for help): ");
     428  
     429  	fputc('\n',stdout);
     430  	rc = get_user_reply(prompt, buf, sizeof(buf));
     431  
     432  	if (rc == -ECANCELED) {
     433  		/* Map ^C and ^D in main menu to 'q' */
     434  		if (is_interactive
     435  		    && fdisk_label_is_changed(fdisk_get_label(cxt, NULL))) {
     436  			/* TRANSLATORS: these yes no questions use rpmatch(),
     437  			 * and should be translated.  */
     438  			rc = get_user_reply(
     439  				_("\nAll unwritten changes will be lost, do you really want to quit? (y/n)"),
     440  				buf, sizeof(buf));
     441  			if (rc || !rpmatch(buf))
     442  				return 0;
     443  		}
     444  		key = 'q';
     445  	} else if (rc) {
     446  		return rc;
     447  	} else
     448  		key = buf[0];
     449  
     450  	ent = get_fdisk_menu_entry(cxt, key, &menu);
     451  	if (!ent) {
     452  		fdisk_warnx(cxt, _("%c: unknown command"), key);
     453  		return -EINVAL;
     454  	}
     455  
     456  	DBG(MENU, ul_debug("selected: key=%c, entry='%s'",
     457  				key, ent->title));
     458  
     459  	/* menu has implemented callback, use it */
     460  	if (menu->callback)
     461  		rc = menu->callback(cxt0, menu, ent);
     462  	else {
     463  		DBG(MENU, ul_debug("no callback for key '%c'", key));
     464  		rc = -EINVAL;
     465  	}
     466  
     467  	DBG(MENU, ul_debug("process menu done [rc=%d]", rc));
     468  	return rc;
     469  }
     470  
     471  static int script_read(struct fdisk_context *cxt)
     472  {
     473  	struct fdisk_script *sc = NULL;
     474  	char *filename = NULL;
     475  	int rc;
     476  
     477  	rc = fdisk_ask_string(cxt, _("Enter script file name"), &filename);
     478  	if (rc)
     479  		return rc;
     480  
     481  	errno = 0;
     482  	sc = fdisk_new_script_from_file(cxt, filename);
     483  	if (!sc && errno)
     484  		fdisk_warn(cxt, _("Cannot open %s"), filename);
     485  	else if (!sc)
     486  		fdisk_warnx(cxt, _("Failed to parse script file %s"), filename);
     487  	else if (fdisk_apply_script(cxt, sc) != 0) {
     488  		fdisk_warnx(cxt, _("Failed to apply script %s"), filename);
     489  		fdisk_warnx(cxt, _("Resetting fdisk!"));
     490  		rc = fdisk_reassign_device(cxt);
     491                  if (rc == 0 && !fdisk_has_label(cxt)) {
     492                          fdisk_info(cxt, _("Device does not contain a recognized partition table."));
     493                          rc = fdisk_create_disklabel(cxt, NULL);
     494  		}
     495  	} else
     496  		fdisk_info(cxt, _("Script successfully applied."));
     497  
     498  	fdisk_unref_script(sc);
     499  	free(filename);
     500  	return rc;
     501  }
     502  
     503  static int script_write(struct fdisk_context *cxt)
     504  {
     505  	struct fdisk_script *sc = NULL;
     506  	char *filename = NULL;
     507  	FILE *f = NULL;
     508  	int rc;
     509  
     510  	rc = fdisk_ask_string(cxt, _("Enter script file name"), &filename);
     511  	if (rc)
     512  		return rc;
     513  
     514  	sc = fdisk_new_script(cxt);
     515  	if (!sc) {
     516  		fdisk_warn(cxt, _("Failed to allocate script handler"));
     517  		goto done;
     518  	}
     519  
     520  	rc = fdisk_script_read_context(sc, NULL);
     521  	if (rc) {
     522  		fdisk_warnx(cxt, _("Failed to transform disk layout into script"));
     523  		goto done;
     524  	}
     525  
     526  	f = fopen(filename, "w");
     527  	if (!f) {
     528  		fdisk_warn(cxt, _("Cannot open %s"), filename);
     529  		goto done;
     530  	}
     531  
     532  	rc = fdisk_script_write_file(sc, f);
     533  	if (rc)
     534  		fdisk_warn(cxt, _("Failed to write script %s"), filename);
     535  	else
     536  		fdisk_info(cxt, _("Script successfully saved."));
     537  done:
     538  	if (f)
     539  		fclose(f);
     540  	fdisk_unref_script(sc);
     541  	free(filename);
     542  	return rc;
     543  }
     544  
     545  static int ask_for_wipe(struct fdisk_context *cxt, size_t partno)
     546  {
     547  	struct fdisk_partition *tmp = NULL;
     548  	char *fstype = NULL;
     549  	int rc, yes = 0;
     550  
     551  	rc = fdisk_get_partition(cxt, partno, &tmp);
     552  	if (rc)
     553  		goto done;
     554  
     555  	rc = fdisk_partition_to_string(tmp, cxt, FDISK_FIELD_FSTYPE, &fstype);
     556  	if (rc || fstype == NULL)
     557  		goto done;
     558  
     559  	fdisk_warnx(cxt, _("Partition #%zu contains a %s signature."), partno + 1, fstype);
     560  
     561  	if (pwipemode == WIPEMODE_AUTO && isatty(STDIN_FILENO))
     562  		fdisk_ask_yesno(cxt, _("Do you want to remove the signature?"), &yes);
     563  	else if (pwipemode == WIPEMODE_ALWAYS)
     564  		yes = 1;
     565  
     566  	if (yes) {
     567  		fdisk_info(cxt, _("The signature will be removed by a write command."));
     568  		rc = fdisk_wipe_partition(cxt, partno, TRUE);
     569  	}
     570  done:
     571  	fdisk_unref_partition(tmp);
     572  	free(fstype);
     573  	return rc;
     574  }
     575  
     576  /*
     577   * Basic fdisk actions
     578   */
     579  static int generic_menu_cb(struct fdisk_context **cxt0,
     580  		       const struct menu *menu __attribute__((__unused__)),
     581  		       const struct menu_entry *ent)
     582  {
     583  	struct fdisk_context *cxt = *cxt0;
     584  	int rc = 0;
     585  	size_t n;
     586  
     587  	/* actions shared between expert and normal mode */
     588  	switch (ent->key) {
     589  	case 'p':
     590  		list_disk_geometry(cxt);
     591  		list_disklabel(cxt);
     592  		break;
     593  	case 'w':
     594  		if (fdisk_is_readonly(cxt)) {
     595  			fdisk_warnx(cxt, _("Device is open in read-only mode."));
     596  			break;
     597  		}
     598  		rc = fdisk_write_disklabel(cxt);
     599  		if (rc)
     600  			err(EXIT_FAILURE, _("failed to write disklabel"));
     601  
     602  		fdisk_info(cxt, _("The partition table has been altered."));
     603  		if (fdisk_get_parent(cxt))
     604  			break; /* nested PT, don't leave */
     605  
     606  		if (device_is_used)
     607  			rc = fdisk_reread_changes(cxt, original_layout);
     608  		else
     609  			rc = fdisk_reread_partition_table(cxt);
     610  		if (!rc)
     611  			rc = fdisk_deassign_device(cxt, 0);
     612  		/* fallthrough */
     613  	case 'q':
     614  		fdisk_unref_context(cxt);
     615  		fputc('\n', stdout);
     616  		exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
     617  	case 'm':
     618  		rc = print_fdisk_menu(cxt);
     619  		break;
     620  	case 'v':
     621  		rc = fdisk_verify_disklabel(cxt);
     622  		break;
     623  	case 'i':
     624  		rc = print_partition_info(cxt);
     625  		break;
     626  	case 'F':
     627  		list_freespace(cxt);
     628  		break;
     629  	}
     630  
     631  	/* expert mode */
     632  	if (ent->expert) {
     633  		switch (ent->key) {
     634  		case 'd':
     635  			dump_firstsector(cxt);
     636  			break;
     637  		case 'D':
     638  			dump_disklabel(cxt);
     639  			break;
     640  		case 'f':
     641  			rc = fdisk_reorder_partitions(cxt);
     642  			break;
     643  		case 'r':
     644  			rc = fdisk_enable_details(cxt, 0);
     645  			break;
     646  		}
     647  		return rc;
     648  	}
     649  
     650  	/* normal mode */
     651  	switch (ent->key) {
     652  	case 'd':
     653  		rc = fdisk_ask_partnum(cxt, &n, FALSE);
     654  		if (rc)
     655  			break; /* no partitions yet (or ENOMEM, ...) */
     656  
     657  		rc = fdisk_delete_partition(cxt, n);
     658  		if (rc)
     659  			fdisk_warnx(cxt, _("Could not delete partition %zu"), n + 1);
     660  		else
     661  			fdisk_info(cxt, _("Partition %zu has been deleted."), n + 1);
     662  		break;
     663  	case 'I':
     664  		script_read(cxt);
     665  		break;
     666  	case 'O':
     667  		script_write(cxt);
     668  		break;
     669  	case 'l':
     670  		list_partition_types(cxt);
     671  		break;
     672  	case 'n':
     673  	{
     674  		size_t partno;
     675  		rc = fdisk_add_partition(cxt, NULL, &partno);
     676  		if (!rc)
     677  			rc = ask_for_wipe(cxt, partno);
     678  		break;
     679  	}
     680  	case 't':
     681  		change_partition_type(cxt);
     682  		break;
     683  	case 'u':
     684  		fdisk_set_unit(cxt,
     685  			fdisk_use_cylinders(cxt) ? "sectors" :
     686  							   "cylinders");
     687  		if (fdisk_use_cylinders(cxt))
     688  			fdisk_info(cxt, _("Changing display/entry units to cylinders (DEPRECATED!)."));
     689  		else
     690  			fdisk_info(cxt, _("Changing display/entry units to sectors."));
     691  		break;
     692  	case 'x':
     693  		fdisk_enable_details(cxt, 1);
     694  		break;
     695  	case 'r':
     696  		/* return from nested BSD to DOS or MBR to GPT */
     697  		if (fdisk_get_parent(cxt)) {
     698  			*cxt0 = fdisk_get_parent(cxt);
     699  
     700  			fdisk_info(cxt, _("Leaving nested disklabel."));
     701  			fdisk_unref_context(cxt);
     702  		}
     703  		break;
     704  	}
     705  
     706  	return rc;
     707  }
     708  
     709  
     710  /*
     711   * This is fdisk frontend for GPT specific libfdisk functions that
     712   * are not exported by generic libfdisk API.
     713   */
     714  static int gpt_menu_cb(struct fdisk_context **cxt0,
     715  		       const struct menu *menu __attribute__((__unused__)),
     716  		       const struct menu_entry *ent)
     717  {
     718  	struct fdisk_context *cxt = *cxt0;
     719  	struct fdisk_context *mbr;
     720  	struct fdisk_partition *pa = NULL;
     721  	size_t n;
     722  	int rc = 0;
     723  	uintmax_t length = 0;
     724  
     725  	assert(cxt);
     726  	assert(ent);
     727  	assert(fdisk_is_label(cxt, GPT));
     728  
     729  	DBG(MENU, ul_debug("enter GPT menu"));
     730  
     731  	if (ent->expert) {
     732  		switch (ent->key) {
     733  		case 'i':
     734  			return fdisk_set_disklabel_id(cxt);
     735  		case 'l':
     736  	                rc =  fdisk_ask_number(cxt, 1, fdisk_get_npartitions(cxt),
     737  	                                ~(uint32_t)0, _("New maximum entries"), &length);
     738  			if (rc)
     739  				return rc;
     740  			return fdisk_gpt_set_npartitions(cxt, (uint32_t) length);
     741  		case 'M':
     742  			mbr = fdisk_new_nested_context(cxt, "dos");
     743  			if (!mbr)
     744  				return -ENOMEM;
     745  			*cxt0 = cxt = mbr;
     746  			if (fdisk_is_details(cxt))
     747  				fdisk_enable_details(cxt, 1);	/* keep us in expert mode */
     748  			fdisk_info(cxt, _("Entering protective/hybrid MBR disklabel."));
     749  			return 0;
     750  		}
     751  
     752  		/* actions where is necessary partnum */
     753  		rc = fdisk_ask_partnum(cxt, &n, FALSE);
     754  		if (rc)
     755  			return rc;
     756  
     757  		switch(ent->key) {
     758  		case 'u':
     759  			pa = fdisk_new_partition();	/* new template */
     760  			if (!pa)
     761  				rc = -ENOMEM;
     762  			else {
     763  				char *str = NULL;
     764  				rc = fdisk_ask_string(cxt, _("New UUID (in 8-4-4-4-12 format)"), &str);
     765  				if (!rc)
     766  					rc = fdisk_partition_set_uuid(pa, str);
     767  				if (!rc)
     768  					rc = fdisk_set_partition(cxt, n, pa);
     769  				free(str);
     770  				fdisk_unref_partition(pa);
     771  			}
     772  			break;
     773  		case 'n':
     774  			pa = fdisk_new_partition();	/* new template */
     775  			if (!pa)
     776  				rc = -ENOMEM;
     777  			else {
     778  				char *str = NULL;
     779  				rc = fdisk_ask_string(cxt, _("New name"), &str);
     780  				if (!rc)
     781  					rc = fdisk_partition_set_name(pa, str);
     782  				if (!rc)
     783  					rc = fdisk_set_partition(cxt, n, pa);
     784  				free(str);
     785  				fdisk_unref_partition(pa);
     786  			}
     787  			break;
     788  		case 'A':
     789  			rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_LEGACYBOOT);
     790  			break;
     791  		case 'B':
     792  			rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_NOBLOCK);
     793  			break;
     794  		case 'R':
     795  			rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_REQUIRED);
     796  			break;
     797  		case 'S':
     798  			rc = fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
     799  			break;
     800  		}
     801  	}
     802  
     803  	return rc;
     804  }
     805  
     806  
     807  /*
     808   * This is fdisk frontend for MBR specific libfdisk functions that
     809   * are not exported by generic libfdisk API.
     810   */
     811  static int dos_menu_cb(struct fdisk_context **cxt0,
     812  		       const struct menu *menu __attribute__((__unused__)),
     813  		       const struct menu_entry *ent)
     814  {
     815  	struct fdisk_context *cxt = *cxt0;
     816  	int rc = 0;
     817  
     818  	DBG(MENU, ul_debug("enter DOS menu"));
     819  
     820  	if (!ent->expert) {
     821  		switch (ent->key) {
     822  		case 'a':
     823  		{
     824  			size_t n;
     825  			rc = fdisk_ask_partnum(cxt, &n, FALSE);
     826  			if (!rc)
     827  				rc = fdisk_toggle_partition_flag(cxt, n, DOS_FLAG_ACTIVE);
     828  			break;
     829  		}
     830  		case 'b':
     831  		{
     832  			struct fdisk_context *bsd
     833  					= fdisk_new_nested_context(cxt, "bsd");
     834  			if (!bsd)
     835  				return -ENOMEM;
     836  			if (!fdisk_has_label(bsd))
     837  				rc = fdisk_create_disklabel(bsd, "bsd");
     838  			if (rc)
     839  				fdisk_unref_context(bsd);
     840  			else {
     841  				*cxt0 = cxt = bsd;
     842  				fdisk_info(cxt,	_("Entering nested BSD disklabel."));
     843  			}
     844  			break;
     845  		}
     846  		case 'c':
     847  			toggle_dos_compatibility_flag(cxt);
     848  			break;
     849  		}
     850  		return rc;
     851  	}
     852  
     853  	/* expert mode */
     854  	switch (ent->key) {
     855  	case 'b':
     856  	{
     857  		size_t n;
     858  		rc = fdisk_ask_partnum(cxt, &n, FALSE);
     859  		if (!rc)
     860  			rc = fdisk_dos_move_begin(cxt, n);
     861  		break;
     862  	}
     863  	case 'i':
     864  		rc = fdisk_set_disklabel_id(cxt);
     865  		break;
     866  	case 'M':
     867  		/* return from nested MBR to GPT (backward compatibility only) */
     868  		if (fdisk_get_parent(cxt)) {
     869  			*cxt0 = fdisk_get_parent(cxt);
     870  
     871  			fdisk_info(cxt, _("Leaving nested disklabel."));
     872  			fdisk_unref_context(cxt);
     873  		}
     874  		break;
     875  	case 'F':
     876  		rc = fdisk_dos_fix_chs(cxt);
     877  		if (rc)
     878  			fdisk_info(cxt, _("C/H/S values fixed."));
     879  		else
     880  			fdisk_info(cxt, _("Nothing to do. C/H/S values are correct already."));
     881  		break;
     882  	}
     883  	return rc;
     884  }
     885  
     886  static int sun_menu_cb(struct fdisk_context **cxt0,
     887  		       const struct menu *menu __attribute__((__unused__)),
     888  		       const struct menu_entry *ent)
     889  {
     890  	struct fdisk_context *cxt = *cxt0;
     891  	int rc = 0;
     892  
     893  	DBG(MENU, ul_debug("enter SUN menu"));
     894  
     895  	assert(cxt);
     896  	assert(ent);
     897  	assert(fdisk_is_label(cxt, SUN));
     898  
     899  	DBG(MENU, ul_debug("enter SUN menu"));
     900  
     901  	/* normal mode */
     902  	if (!ent->expert) {
     903  		size_t n;
     904  
     905  		rc = fdisk_ask_partnum(cxt, &n, FALSE);
     906  		if (rc)
     907  			return rc;
     908  		switch (ent->key) {
     909  		case 'a':
     910  			rc = fdisk_toggle_partition_flag(cxt, n, SUN_FLAG_RONLY);
     911  			break;
     912  		case 'c':
     913  			rc = fdisk_toggle_partition_flag(cxt, n, SUN_FLAG_UNMNT);
     914  			break;
     915  		}
     916  		return rc;
     917  	}
     918  
     919  	/* expert mode */
     920  	switch (ent->key) {
     921  	case 'a':
     922  		rc = fdisk_sun_set_alt_cyl(cxt);
     923  		break;
     924  	case 'e':
     925  		rc = fdisk_sun_set_xcyl(cxt);
     926  		break;
     927  	case 'i':
     928  		rc = fdisk_sun_set_ilfact(cxt);
     929  		break;
     930  	case 'o':
     931  		rc = fdisk_sun_set_rspeed(cxt);
     932  		break;
     933  	case 'y':
     934  		rc = fdisk_sun_set_pcylcount(cxt);
     935  		break;
     936  	}
     937  	return rc;
     938  }
     939  
     940  static int sgi_menu_cb(struct fdisk_context **cxt0,
     941  		       const struct menu *menu __attribute__((__unused__)),
     942  		       const struct menu_entry *ent)
     943  {
     944  	struct fdisk_context *cxt = *cxt0;
     945  	int rc = -EINVAL;
     946  	size_t n = 0;
     947  
     948  	DBG(MENU, ul_debug("enter SGI menu"));
     949  
     950  	assert(cxt);
     951  	assert(ent);
     952  	assert(fdisk_is_label(cxt, SGI));
     953  
     954  	if (ent->expert)
     955  		return rc;
     956  
     957  	switch (ent->key) {
     958  	case 'a':
     959  		rc = fdisk_ask_partnum(cxt, &n, FALSE);
     960  		if (!rc)
     961  			rc = fdisk_toggle_partition_flag(cxt, n, SGI_FLAG_BOOT);
     962  		break;
     963  	case 'b':
     964  		fdisk_sgi_set_bootfile(cxt);
     965  		break;
     966  	case 'c':
     967  		rc = fdisk_ask_partnum(cxt, &n, FALSE);
     968  		if (!rc)
     969  			rc = fdisk_toggle_partition_flag(cxt, n, SGI_FLAG_SWAP);
     970  		break;
     971  	case 'i':
     972  		rc = fdisk_sgi_create_info(cxt);
     973  		break;
     974  	}
     975  
     976  	return rc;
     977  }
     978  
     979  /*
     980   * This is fdisk frontend for BSD specific libfdisk functions that
     981   * are not exported by generic libfdisk API.
     982   */
     983  static int bsd_menu_cb(struct fdisk_context **cxt0,
     984  		       const struct menu *menu __attribute__((__unused__)),
     985  		       const struct menu_entry *ent)
     986  {
     987  	struct fdisk_context *cxt = *cxt0;
     988  	int rc = 0, org;
     989  
     990  	assert(cxt);
     991  	assert(ent);
     992  	assert(fdisk_is_label(cxt, BSD));
     993  
     994  	DBG(MENU, ul_debug("enter BSD menu"));
     995  
     996  	switch(ent->key) {
     997  	case 'e':
     998  		rc = fdisk_bsd_edit_disklabel(cxt);
     999  		break;
    1000  	case 'i':
    1001  		rc = fdisk_bsd_write_bootstrap(cxt);
    1002  		break;
    1003  	case 's':
    1004  		org = fdisk_is_details(cxt);
    1005  
    1006  		fdisk_enable_details(cxt, 1);
    1007  		list_disklabel(cxt);
    1008  		fdisk_enable_details(cxt, org);
    1009  		break;
    1010  	case 'x':
    1011  		rc = fdisk_bsd_link_partition(cxt);
    1012  		break;
    1013  	}
    1014  	return rc;
    1015  }
    1016  
    1017  /* C/H/S commands
    1018   *
    1019   * The geometry setting from this dialog is not persistent and maybe reset by
    1020   * fdisk_reset_device_properties() (for example when you create a new disk
    1021   * label). Note that on command line specified -C/-H/-S setting is persistent
    1022   * as it's based on fdisk_save_user_geometry().
    1023   */
    1024  static int geo_menu_cb(struct fdisk_context **cxt0,
    1025  		       const struct menu *menu __attribute__((__unused__)),
    1026  		       const struct menu_entry *ent)
    1027  {
    1028  	struct fdisk_context *cxt = *cxt0;
    1029  	struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
    1030  	int rc = -EINVAL;
    1031  	uintmax_t c = 0, h = 0, s = 0;
    1032  	fdisk_sector_t mi, ma;
    1033  
    1034  	DBG(MENU, ul_debug("enter GEO menu"));
    1035  
    1036  	assert(cxt);
    1037  	assert(ent);
    1038  
    1039  	/* default */
    1040  	if (!lb)
    1041  		lb = fdisk_get_label(cxt, "dos");
    1042  
    1043  	switch (ent->key) {
    1044  	case 'c':
    1045  		fdisk_label_get_geomrange_cylinders(lb, &mi, &ma);
    1046  		rc =  fdisk_ask_number(cxt, mi, fdisk_get_geom_cylinders(cxt),
    1047  				ma, _("Number of cylinders"), &c);
    1048  		break;
    1049  	case 'h':
    1050  	{
    1051  		unsigned int i, a;
    1052  		fdisk_label_get_geomrange_heads(lb, &i, &a);
    1053  		rc =  fdisk_ask_number(cxt, i, fdisk_get_geom_heads(cxt),
    1054  				a, _("Number of heads"), &h);
    1055  		break;
    1056  	}
    1057  	case 's':
    1058  		fdisk_label_get_geomrange_sectors(lb, &mi, &ma);
    1059  		rc =  fdisk_ask_number(cxt, mi, fdisk_get_geom_sectors(cxt),
    1060  				ma, _("Number of sectors"), &s);
    1061  		break;
    1062  	}
    1063  
    1064  	if (!rc)
    1065  		fdisk_override_geometry(cxt, c, h, s);
    1066  	return rc;
    1067  }
    1068  
    1069  static int createlabel_menu_cb(struct fdisk_context **cxt0,
    1070  		       const struct menu *menu __attribute__((__unused__)),
    1071  		       const struct menu_entry *ent)
    1072  {
    1073  	struct fdisk_context *cxt = *cxt0;
    1074  	const char *wanted = NULL;
    1075  	int rc = -EINVAL;
    1076  
    1077  	DBG(MENU, ul_debug("enter Create label menu"));
    1078  
    1079  	assert(cxt);
    1080  	assert(ent);
    1081  
    1082  	if (ent->expert) {
    1083  		switch (ent->key) {
    1084  		case 'g':
    1085  			/* Deprecated, use 'G' in main menu, just for backward
    1086  			 * compatibility only. */
    1087  			wanted = "sgi";
    1088  			break;
    1089  		}
    1090  	} else {
    1091  		switch (ent->key) {
    1092  			case 'g':
    1093  				wanted = "gpt";
    1094  				break;
    1095  			case 'G':
    1096  				wanted = "sgi";
    1097  				break;
    1098  			case 'o':
    1099  				wanted = "dos";
    1100  				break;
    1101  			case 's':
    1102  				wanted = "sun";
    1103  				break;
    1104  		}
    1105  	}
    1106  
    1107  	if (wanted) {
    1108  		rc = fdisk_create_disklabel(cxt, wanted);
    1109  		if (rc) {
    1110  			errno = -rc;
    1111  			fdisk_warn(cxt, _("Failed to create '%s' disk label"), wanted);
    1112  		}
    1113  	}
    1114  	if (rc == 0 && fdisk_get_collision(cxt))
    1115  		follow_wipe_mode(cxt);
    1116  
    1117  	return rc;
    1118  }