(root)/
util-linux-2.39/
disk-utils/
fdisk.c
       1  /*
       2   * Copyright (C) 1992  A. V. Le Blanc (LeBlanc@mcc.ac.uk)
       3   * Copyright (C) 2012  Davidlohr Bueso <dave@gnu.org>
       4   *
       5   * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
       6   *
       7   * This program is free software.  You can redistribute it and/or
       8   * modify it under the terms of the GNU General Public License as
       9   * published by the Free Software Foundation: either version 1 or
      10   * (at your option) any later version.
      11   */
      12  #include <unistd.h>
      13  #include <stdio.h>
      14  #include <stdlib.h>
      15  #include <string.h>
      16  #include <fcntl.h>
      17  #include <ctype.h>
      18  #include <errno.h>
      19  #include <getopt.h>
      20  #include <sys/stat.h>
      21  #include <sys/time.h>
      22  #include <time.h>
      23  #include <limits.h>
      24  #include <signal.h>
      25  #include <poll.h>
      26  #include <libsmartcols.h>
      27  #ifdef HAVE_LIBREADLINE
      28  # define _FUNCTION_DEF
      29  # include <readline/readline.h>
      30  #endif
      31  
      32  #include "c.h"
      33  #include "xalloc.h"
      34  #include "all-io.h"
      35  #include "nls.h"
      36  #include "rpmatch.h"
      37  #include "blkdev.h"
      38  #include "mbsalign.h"
      39  #include "pathnames.h"
      40  #include "canonicalize.h"
      41  #include "strutils.h"
      42  #include "closestream.h"
      43  #include "pager.h"
      44  
      45  #include "fdisk.h"
      46  
      47  #include "pt-sun.h"		/* to toggle flags */
      48  
      49  #ifdef HAVE_LINUX_COMPILER_H
      50  # include <linux/compiler.h>
      51  #endif
      52  #ifdef HAVE_LINUX_BLKPG_H
      53  # include <linux/blkpg.h>
      54  #endif
      55  
      56  int pwipemode = WIPEMODE_AUTO;
      57  int device_is_used;
      58  int is_interactive;
      59  struct fdisk_table *original_layout;
      60  
      61  static int wipemode = WIPEMODE_AUTO;
      62  
      63  /*
      64   * fdisk debug stuff (see fdisk.h and include/debug.h)
      65   */
      66  UL_DEBUG_DEFINE_MASK(fdisk);
      67  UL_DEBUG_DEFINE_MASKNAMES(fdisk) = UL_DEBUG_EMPTY_MASKNAMES;
      68  
      69  static void fdiskprog_init_debug(void)
      70  {
      71  	__UL_INIT_DEBUG_FROM_ENV(fdisk, FDISKPROG_DEBUG_, 0, FDISK_DEBUG);
      72  }
      73  
      74  static void reply_sighandler(int sig __attribute__((unused)))
      75  {
      76  	DBG(ASK, ul_debug("got signal"));
      77  }
      78  
      79  static int reply_running;
      80  
      81  #ifdef HAVE_LIBREADLINE
      82  static char *reply_line;
      83  
      84  static void reply_linehandler(char *line)
      85  {
      86  	reply_line = line;
      87  	reply_running = 0;
      88  	rl_callback_handler_remove();	/* avoid duplicate prompt */
      89  }
      90  #endif
      91  
      92  int get_user_reply(const char *prompt, char *buf, size_t bufsz)
      93  {
      94  	struct sigaction oldact, act = {
      95  		.sa_handler = reply_sighandler
      96  	};
      97  	struct pollfd fds[] = {
      98  		{ .fd = fileno(stdin), .events = POLLIN }
      99  	};
     100  	size_t sz;
     101  	int ret = 0;
     102  
     103  	DBG(ASK, ul_debug("asking for user reply %s", is_interactive ? "[interactive]" : ""));
     104  
     105  	sigemptyset(&act.sa_mask);
     106  	sigaction(SIGINT, &act, &oldact);
     107  
     108  #ifdef HAVE_LIBREADLINE
     109  	if (is_interactive)
     110  		rl_callback_handler_install(prompt, reply_linehandler);
     111  #endif
     112  	errno = 0;
     113  	reply_running = 1;
     114  	do {
     115  		int rc;
     116  
     117  		*buf = '\0';
     118  #ifdef HAVE_LIBREADLINE
     119  		if (!is_interactive)
     120  #endif
     121  		{
     122  			fputs(prompt, stdout);
     123  			fflush(stdout);
     124  		}
     125  
     126  		rc = poll(fds, 1, -1);
     127  		if (rc == -1 && errno == EINTR)	{	/* interrupted by signal */
     128  			DBG(ASK, ul_debug("cancel by CTRL+C"));
     129  			ret = -ECANCELED;
     130  			goto done;
     131  		}
     132  		if (rc == -1 && errno != EAGAIN) {	/* error */
     133  			ret = -errno;
     134  			goto done;
     135  		}
     136  #ifdef HAVE_LIBREADLINE
     137  		if (is_interactive) {
     138  			/* read input and copy to buf[] */
     139  			rl_callback_read_char();
     140  			if (!reply_running && reply_line) {
     141  				sz = strlen(reply_line);
     142  				if (sz == 0)
     143  					buf[sz++] = '\n';
     144  				else
     145  					memcpy(buf, reply_line, min(sz, bufsz));
     146  				buf[min(sz, bufsz - 1)] = '\0';
     147  				free(reply_line);
     148  				reply_line = NULL;
     149  			}
     150  		} else
     151  #endif
     152  		{
     153  			if (!fgets(buf, bufsz, stdin))
     154  				*buf = '\0';
     155  			break;
     156  		}
     157  	} while (reply_running);
     158  
     159  	if (!*buf) {
     160  		DBG(ASK, ul_debug("cancel by CTRL+D"));
     161  		ret = -ECANCELED;
     162  		clearerr(stdin);
     163  		goto done;
     164  	}
     165  
     166  	/*
     167  	 * cleanup the reply
     168  	 */
     169  	sz = ltrim_whitespace((unsigned char *) buf);
     170  	if (sz && *(buf + sz - 1) == '\n')
     171  		*(buf + sz - 1) = '\0';
     172  
     173  done:
     174  #ifdef HAVE_LIBREADLINE
     175  	if (is_interactive)
     176  		rl_callback_handler_remove();
     177  #endif
     178  	sigaction(SIGINT, &oldact, NULL);
     179  	DBG(ASK, ul_debug("user's reply: >>>%s<<< [rc=%d]", buf, ret));
     180  	return ret;
     181  }
     182  
     183  static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask,
     184  		    char *buf, size_t bufsz)
     185  
     186  {
     187  	const char *q = fdisk_ask_get_query(ask);
     188  	int dft = fdisk_ask_menu_get_default(ask);
     189  
     190  	if (q) {
     191  		fputs(q, stdout);		/* print header */
     192  		fputc('\n', stdout);
     193  	}
     194  
     195  	do {
     196  		char prompt[128];
     197  		int key, c, rc;
     198  		const char *name, *desc;
     199  		size_t i = 0;
     200  
     201  		/* print menu items */
     202  		while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0)
     203  			fprintf(stdout, "   %c   %s (%s)\n", key, name, desc);
     204  
     205  		/* ask for key */
     206  		snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft);
     207  		rc = get_user_reply(prompt, buf, bufsz);
     208  		if (rc)
     209  			return rc;
     210  		if (!*buf) {
     211  			fdisk_info(cxt, _("Using default response %c."), dft);
     212  			c = dft;
     213  		} else
     214  			c = tolower(buf[0]);
     215  
     216  		/* check result */
     217  		i = 0;
     218  		while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) {
     219  			if (c == key) {
     220  				fdisk_ask_menu_set_result(ask, c);
     221  				return 0;	/* success */
     222  			}
     223  		}
     224  		fdisk_warnx(cxt, _("Value out of range."));
     225  	} while (1);
     226  
     227  	return -EINVAL;
     228  }
     229  
     230  
     231  #define tochar(num)	((int) ('a' + num - 1))
     232  static int ask_number(struct fdisk_context *cxt,
     233  		      struct fdisk_ask *ask,
     234  		      char *buf, size_t bufsz)
     235  {
     236  	char prompt[128] = { '\0' };
     237  	const char *q = fdisk_ask_get_query(ask);
     238  	const char *range = fdisk_ask_number_get_range(ask);
     239  
     240  	uint64_t dflt = fdisk_ask_number_get_default(ask),
     241  		 low = fdisk_ask_number_get_low(ask),
     242  		 high = fdisk_ask_number_get_high(ask);
     243  	int inchar = fdisk_ask_number_inchars(ask);
     244  
     245  	assert(q);
     246  
     247  	DBG(ASK, ul_debug("asking for number "
     248  			"['%s', <%"PRIu64",%"PRIu64">, default=%"PRIu64", range: %s]",
     249  			q, low, high, dflt, range));
     250  
     251  	if (range && dflt >= low && dflt <= high) {
     252  		if (inchar)
     253  			snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "),
     254  					q, range, tochar(dflt));
     255  		else
     256  			snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
     257  					q, range, dflt);
     258  
     259  	} else if (dflt >= low && dflt <= high) {
     260  		if (inchar)
     261  			snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "),
     262  					q, tochar(low), tochar(high), tochar(dflt));
     263  		else
     264  			snprintf(prompt, sizeof(prompt),
     265  					_("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
     266  					q, low, high, dflt);
     267  	} else if (inchar)
     268  		snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "),
     269  				q, tochar(low), tochar(high));
     270  	else
     271  		snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
     272  				q, low, high);
     273  
     274  	do {
     275  		int rc = get_user_reply(prompt, buf, bufsz);
     276  		uint64_t num = 0;
     277  
     278  		if (rc)
     279  			return rc;
     280  		if (!*buf && dflt >= low && dflt <= high)
     281  			return fdisk_ask_number_set_result(ask, dflt);
     282  
     283  		if (isdigit_string(buf)) {
     284  			char *end;
     285  
     286  			errno = 0;
     287  			num = strtoumax(buf, &end, 10);
     288  			if (errno || buf == end || (end && *end))
     289  				continue;
     290  		} else if (inchar && isalpha(*buf)) {
     291  			num = tolower(*buf) - 'a' + 1;
     292  		} else
     293  			rc = -EINVAL;
     294  
     295  		if (rc == 0 && num >= low && num <= high)
     296  			return fdisk_ask_number_set_result(ask, num);
     297  
     298  		fdisk_warnx(cxt, _("Value out of range."));
     299  	} while (1);
     300  
     301  	return -1;
     302  }
     303  
     304  static int ask_offset(struct fdisk_context *cxt,
     305  		      struct fdisk_ask *ask,
     306  		      char *buf, size_t bufsz)
     307  {
     308  	char prompt[128] = { '\0' };
     309  	const char *q = fdisk_ask_get_query(ask);
     310  	const char *range = fdisk_ask_number_get_range(ask);
     311  
     312  	uint64_t dflt = fdisk_ask_number_get_default(ask),
     313  		 low = fdisk_ask_number_get_low(ask),
     314  		 high = fdisk_ask_number_get_high(ask),
     315  		 base = fdisk_ask_number_get_base(ask);
     316  
     317  	assert(q);
     318  
     319  	DBG(ASK, ul_debug("asking for offset ['%s', <%"PRIu64",%"PRIu64">, base=%"PRIu64", default=%"PRIu64", range: %s]",
     320  				q, low, high, base, dflt, range));
     321  
     322  	if (range && dflt >= low && dflt <= high)
     323  		snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
     324  		         q, range, dflt);
     325  	else if (dflt >= low && dflt <= high)
     326  		snprintf(prompt, sizeof(prompt),
     327  		         _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
     328  		         q, low, high, dflt);
     329  	else
     330  		snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
     331  		         q, low, high);
     332  
     333  	do {
     334  		uintmax_t num = 0;
     335  		char sig = 0, *p;
     336  		int pwr = 0;
     337  
     338  		int rc = get_user_reply(prompt, buf, bufsz);
     339  		if (rc)
     340  			return rc;
     341  		if (!*buf && dflt >= low && dflt <= high)
     342  			return fdisk_ask_number_set_result(ask, dflt);
     343  
     344  		p = buf;
     345  		if (*p == '+' || *p == '-') {
     346  			sig = *buf;
     347  			p++;
     348  		}
     349  
     350  		rc = parse_size(p, &num, &pwr);
     351  		if (rc)
     352  			continue;
     353  		DBG(ASK, ul_debug("parsed size: %ju", num));
     354  		if (sig && pwr) {
     355  			/* +{size}{K,M,...} specified, the "num" is in bytes */
     356  			uint64_t unit = fdisk_ask_number_get_unit(ask);
     357  			num += unit/2;	/* round */
     358  			num /= unit;
     359  		}
     360  		if (sig == '+')
     361  			num += base;
     362  		else if (sig == '-' && fdisk_ask_number_is_wrap_negative(ask))
     363  			num = high - num;
     364  		else if (sig == '-')
     365  			num = base - num;
     366  
     367  		DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]",
     368  				num, sig, pwr,
     369  				sig ? "relative" : "absolute"));
     370  		if (num >= low && num <= high) {
     371  			if (sig && pwr)
     372  				fdisk_ask_number_set_relative(ask, 1);
     373  			return fdisk_ask_number_set_result(ask, (uint64_t)num);
     374  		}
     375  		fdisk_warnx(cxt, _("Value out of range."));
     376  	} while (1);
     377  
     378  	return -1;
     379  }
     380  
     381  static unsigned int info_count;
     382  
     383  static void fputs_info(struct fdisk_ask *ask, FILE *out)
     384  {
     385  	const char *msg;
     386  	assert(ask);
     387  
     388  	msg = fdisk_ask_print_get_mesg(ask);
     389  	if (!msg)
     390  		return;
     391  	if (info_count == 1)
     392  		fputc('\n', out);
     393  
     394  	fputs(msg, out);
     395  	fputc('\n', out);
     396  }
     397  
     398  int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
     399  		    void *data __attribute__((__unused__)))
     400  {
     401  	int rc = 0;
     402  	char buf[BUFSIZ] = { '\0' };
     403  
     404  	assert(cxt);
     405  	assert(ask);
     406  
     407  	if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO)
     408  		info_count = 0;
     409  
     410  	switch(fdisk_ask_get_type(ask)) {
     411  	case FDISK_ASKTYPE_MENU:
     412  		return ask_menu(cxt, ask, buf, sizeof(buf));
     413  	case FDISK_ASKTYPE_NUMBER:
     414  		return ask_number(cxt, ask, buf, sizeof(buf));
     415  	case FDISK_ASKTYPE_OFFSET:
     416  		return ask_offset(cxt, ask, buf, sizeof(buf));
     417  	case FDISK_ASKTYPE_INFO:
     418  		if (!fdisk_is_listonly(cxt))
     419  			info_count++;
     420  		fputs_info(ask, stdout);
     421  		break;
     422  	case FDISK_ASKTYPE_WARNX:
     423  		fflush(stdout);
     424  		color_scheme_fenable("warn", UL_COLOR_RED, stderr);
     425  		fputs(fdisk_ask_print_get_mesg(ask), stderr);
     426  		color_fdisable(stderr);
     427  		fputc('\n', stderr);
     428  		break;
     429  	case FDISK_ASKTYPE_WARN:
     430  		fflush(stdout);
     431  		color_scheme_fenable("warn", UL_COLOR_RED, stderr);
     432  		fputs(fdisk_ask_print_get_mesg(ask), stderr);
     433  		errno = fdisk_ask_print_get_errno(ask);
     434  		fprintf(stderr, ": %m\n");
     435  		color_fdisable(stderr);
     436  		break;
     437  	case FDISK_ASKTYPE_YESNO:
     438  		fputc('\n', stdout);
     439  		do {
     440  			int x;
     441  			fputs(fdisk_ask_get_query(ask), stdout);
     442  			rc = get_user_reply(_(" [Y]es/[N]o: "), buf, sizeof(buf));
     443  			if (rc)
     444  				break;
     445  			x = rpmatch(buf);
     446  			if (x == RPMATCH_YES || x == RPMATCH_NO) {
     447  				fdisk_ask_yesno_set_result(ask, x);
     448  				break;
     449  			}
     450  		} while(1);
     451  		DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc));
     452  		break;
     453  	case FDISK_ASKTYPE_STRING:
     454  	{
     455  		char prmt[BUFSIZ];
     456  		snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask));
     457  		fputc('\n', stdout);
     458  		rc = get_user_reply(prmt, buf, sizeof(buf));
     459  		if (rc == 0)
     460  			fdisk_ask_string_set_result(ask, xstrdup(buf));
     461  		DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc));
     462  		break;
     463  	}
     464  	default:
     465  		warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
     466  		return -EINVAL;
     467  	}
     468  	return rc;
     469  }
     470  
     471  static struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt, int *canceled)
     472  {
     473  	const char *q;
     474  	struct fdisk_label *lb;
     475  
     476  	assert(cxt);
     477  	lb = fdisk_get_label(cxt, NULL);
     478  
     479  	if (!lb)
     480  		return NULL;
     481  
     482  	*canceled = 0;
     483  
     484  	if (fdisk_label_has_parttypes_shortcuts(lb))
     485  		 q = fdisk_label_has_code_parttypes(lb) ?
     486  			_("Hex code or alias (type L to list all): ") :
     487  			_("Partition type or alias (type L to list all): ");
     488  	else
     489  	        q = fdisk_label_has_code_parttypes(lb) ?
     490  			_("Hex code (type L to list all codes): ") :
     491  			_("Partition type (type L to list all types): ");
     492  	do {
     493  		char buf[256] = { '\0' };
     494  		int rc = get_user_reply(q, buf, sizeof(buf));
     495  
     496  		if (rc) {
     497  			if (rc == -ECANCELED)
     498  				*canceled = 1;
     499  			break;
     500  		}
     501  
     502  		if (buf[1] == '\0' && toupper(*buf) == 'L')
     503  			list_partition_types(cxt);
     504  		else if (*buf) {
     505  			struct fdisk_parttype *t = fdisk_label_advparse_parttype(lb, buf,
     506  								FDISK_PARTTYPE_PARSE_DATA
     507  								| FDISK_PARTTYPE_PARSE_ALIAS
     508  								| FDISK_PARTTYPE_PARSE_NAME
     509  								| FDISK_PARTTYPE_PARSE_SEQNUM);
     510  			if (!t)
     511  				fdisk_info(cxt, _("Failed to parse '%s' partition type."), buf);
     512  			return t;
     513  		}
     514          } while (1);
     515  
     516  	return NULL;
     517  }
     518  
     519  
     520  void list_partition_types(struct fdisk_context *cxt)
     521  {
     522  	size_t ntypes = 0, next = 0;
     523  	struct fdisk_label *lb;
     524  	int pager = 0;
     525  
     526  	assert(cxt);
     527  	lb = fdisk_get_label(cxt, NULL);
     528  	if (!lb)
     529  		return;
     530  	ntypes = fdisk_label_get_nparttypes(lb);
     531  	if (!ntypes)
     532  		return;
     533  
     534  	if (fdisk_label_has_code_parttypes(lb)) {
     535  		/*
     536  		 * Prints in 4 columns in format <hex> <name>
     537  		 */
     538  		size_t last[4], done = 0, size;
     539  		int i;
     540  
     541  		size = ntypes;
     542  
     543  		for (i = 3; i >= 0; i--)
     544  			last[3 - i] = done += (size + i - done) / (i + 1);
     545  		i = done = 0;
     546  
     547  		do {
     548  			#define NAME_WIDTH 15
     549  			char name[NAME_WIDTH * MB_LEN_MAX];
     550  			size_t width = NAME_WIDTH;
     551  			const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, next);
     552  			size_t ret;
     553  
     554  			if (fdisk_parttype_get_name(t)) {
     555  				printf("%s%02x ", i ? "  " : "\n",
     556  						fdisk_parttype_get_code(t));
     557  				ret = mbsalign(_(fdisk_parttype_get_name(t)),
     558  						name, sizeof(name),
     559  						&width, MBS_ALIGN_LEFT, 0);
     560  
     561  				if (ret == (size_t)-1 || ret >= sizeof(name))
     562  					printf("%-15.15s",
     563  						_(fdisk_parttype_get_name(t)));
     564  				else
     565  					fputs(name, stdout);
     566  			}
     567  
     568  			next = last[i++] + done;
     569  			if (i > 3 || next >= last[i]) {
     570  				i = 0;
     571  				next = ++done;
     572  			}
     573  		} while (done < last[0]);
     574  
     575  		putchar('\n');
     576  	} else {
     577  		/*
     578  		 * Prints 1 column in format <idx> <name> <typestr>
     579  		 */
     580  		size_t i;
     581  
     582  		pager_open();
     583  		pager = 1;
     584  
     585  		for (i = 0; i < ntypes; i++) {
     586  			const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, i);
     587  			printf("%3zu %-30s %s\n", i + 1,
     588  					fdisk_parttype_get_name(t),
     589  					fdisk_parttype_get_string(t));
     590  		}
     591  
     592  	}
     593  
     594  
     595  	/*
     596  	 * Aliases
     597  	 */
     598  	if (fdisk_label_has_parttypes_shortcuts(lb)) {
     599  		const char *alias = NULL, *typestr = NULL;
     600  		int rc = 0;
     601  
     602  		fputs(_("\nAliases:\n"), stdout);
     603  
     604  		for (next = 0; rc == 0 || rc == 2; next++) {
     605  			/* rc: <0 error, 0 success, 1 end, 2 deprecated */
     606  			rc = fdisk_label_get_parttype_shortcut(lb,
     607  					next, &typestr, NULL, &alias);
     608  			if (rc == 0)
     609  				printf("   %-14s - %s\n", alias, typestr);
     610  		}
     611  	}
     612  
     613  	if (pager)
     614  		pager_close();
     615  
     616  }
     617  
     618  void toggle_dos_compatibility_flag(struct fdisk_context *cxt)
     619  {
     620  	struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
     621  	int flag;
     622  
     623  	if (!lb)
     624  		return;
     625  
     626  	flag = !fdisk_dos_is_compatible(lb);
     627  	fdisk_info(cxt, flag ?
     628  			_("DOS Compatibility flag is set (DEPRECATED!)") :
     629  			_("DOS Compatibility flag is not set"));
     630  
     631  	fdisk_dos_enable_compatible(lb, flag);
     632  
     633  	if (fdisk_is_label(cxt, DOS))
     634  		fdisk_reset_alignment(cxt);	/* reset the current label */
     635  }
     636  
     637  void change_partition_type(struct fdisk_context *cxt)
     638  {
     639  	size_t i;
     640  	struct fdisk_parttype *t = NULL;
     641  	struct fdisk_partition *pa = NULL;
     642  	const char *old = NULL;
     643  	int canceled = 0;
     644  
     645  	assert(cxt);
     646  
     647  	if (fdisk_ask_partnum(cxt, &i, FALSE))
     648  		return;
     649  
     650  	if (fdisk_get_partition(cxt, i, &pa)) {
     651  		fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
     652  		return;
     653  	}
     654  
     655  	t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
     656  	old = t ? fdisk_parttype_get_name(t) : _("Unknown");
     657  
     658  	do {
     659  		t = ask_partition_type(cxt, &canceled);
     660  		if (canceled)
     661  			break;
     662  	} while (!t);
     663  
     664  	if (canceled == 0 && t && fdisk_set_partition_type(cxt, i, t) == 0)
     665  		fdisk_info(cxt,
     666  			_("Changed type of partition '%s' to '%s'."),
     667  			old, t ? fdisk_parttype_get_name(t) : _("Unknown"));
     668  	else
     669  		fdisk_info(cxt,
     670  			_("Type of partition %zu is unchanged: %s."),
     671  			i + 1, old);
     672  
     673  	fdisk_unref_partition(pa);
     674  	fdisk_unref_parttype(t);
     675  }
     676  
     677  int print_partition_info(struct fdisk_context *cxt)
     678  {
     679  	struct fdisk_partition *pa = NULL;
     680  	int rc = 0;
     681  	size_t i, nfields;
     682  	int *fields = NULL;
     683  	struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
     684  
     685  	if ((rc = fdisk_ask_partnum(cxt, &i, FALSE)))
     686  		return rc;
     687  
     688  	if ((rc = fdisk_get_partition(cxt, i, &pa))) {
     689  		fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
     690  		return rc;
     691  	}
     692  
     693  	if ((rc = fdisk_label_get_fields_ids_all(lb, cxt, &fields, &nfields)))
     694  		goto clean_data;
     695  
     696  	for (i = 0; i < nfields; ++i) {
     697  		int id = fields[i];
     698  		char *data = NULL;
     699  		const struct fdisk_field *fd = fdisk_label_get_field(lb, id);
     700  
     701  		if (!fd)
     702  			continue;
     703  
     704  		rc = fdisk_partition_to_string(pa, cxt, id, &data);
     705  		if (rc < 0)
     706  			goto clean_data;
     707  		if (!data || !*data)
     708  			continue;
     709  		fdisk_info(cxt, "%15s: %s", fdisk_field_get_name(fd), data);
     710  		free(data);
     711  	}
     712  
     713  clean_data:
     714  	fdisk_unref_partition(pa);
     715  	free(fields);
     716  	return rc;
     717  }
     718  
     719  static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz)
     720  {
     721  	size_t next;
     722  	const unsigned char *p0 = buf + i;
     723  
     724  	for (next = i + 16; next < sz; next += 16) {
     725  		if (memcmp(p0, buf + next, 16) != 0)
     726  			break;
     727  	}
     728  
     729  	return next == i + 16 ? i : next;
     730  }
     731  
     732  static void dump_buffer(off_t base, unsigned char *buf, size_t sz)
     733  {
     734  	size_t i, l, next = 0;
     735  
     736  	if (!buf)
     737  		return;
     738  	for (i = 0, l = 0; i < sz; i++, l++) {
     739  		if (l == 0) {
     740  			if (!next)
     741  				next = skip_empty(buf, i, sz);
     742  			printf("%08jx ", (intmax_t)base + i);
     743  		}
     744  		printf(" %02x", buf[i]);
     745  		if (l == 7)				/* words separator */
     746  			fputs(" ", stdout);
     747  		else if (l == 15) {
     748  			fputc('\n', stdout);		/* next line */
     749  			l = -1;
     750  			if (next > i) {
     751  				printf("*\n");
     752  				i = next - 1;
     753  			}
     754  			next = 0;
     755  		}
     756  	}
     757  	if (l > 0)
     758  		printf("\n");
     759  }
     760  
     761  static void dump_blkdev(struct fdisk_context *cxt, const char *name,
     762  			uint64_t offset, size_t size)
     763  {
     764  	int fd = fdisk_get_devfd(cxt);
     765  
     766  	fdisk_info(cxt, _("\n%s: offset = %"PRIu64", size = %zu bytes."),
     767  			name, offset, size);
     768  
     769  	assert(fd >= 0);
     770  
     771  	if (lseek(fd, (off_t) offset, SEEK_SET) == (off_t) -1)
     772  		fdisk_warn(cxt, _("cannot seek"));
     773  	else {
     774  		unsigned char *buf = xmalloc(size);
     775  
     776  		if (read_all(fd, (char *) buf, size) != (ssize_t) size)
     777  			fdisk_warn(cxt, _("cannot read"));
     778  		else
     779  			dump_buffer(offset, buf, size);
     780  		free(buf);
     781  	}
     782  }
     783  
     784  void dump_firstsector(struct fdisk_context *cxt)
     785  {
     786  	assert(cxt);
     787  
     788  	dump_blkdev(cxt, _("First sector"), 0, fdisk_get_sector_size(cxt));
     789  }
     790  
     791  void dump_disklabel(struct fdisk_context *cxt)
     792  {
     793  	int i = 0;
     794  	const char *name = NULL;
     795  	uint64_t offset = 0;
     796  	size_t size = 0;
     797  
     798  	assert(cxt);
     799  
     800  	while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size)
     801  		dump_blkdev(cxt, name, offset, size);
     802  }
     803  
     804  static fdisk_sector_t get_dev_blocks(char *dev)
     805  {
     806  	int fd, ret;
     807  	fdisk_sector_t size;
     808  
     809  	if ((fd = open(dev, O_RDONLY|O_NONBLOCK)) < 0)
     810  		err(EXIT_FAILURE, _("cannot open %s"), dev);
     811  	ret = blkdev_get_sectors(fd, (unsigned long long *) &size);
     812  	close(fd);
     813  	if (ret < 0)
     814  		err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
     815  	return size/2;
     816  }
     817  
     818  
     819  void follow_wipe_mode(struct fdisk_context *cxt)
     820  {
     821  	int dowipe = wipemode == WIPEMODE_ALWAYS ? 1 : 0;
     822  
     823  	if (isatty(STDIN_FILENO) && wipemode == WIPEMODE_AUTO)
     824  		dowipe = 1;	/* do it in interactive mode */
     825  
     826  	if (fdisk_is_ptcollision(cxt) && wipemode != WIPEMODE_NEVER)
     827  		dowipe = 1;	/* always remove old PT */
     828  
     829  	fdisk_enable_wipe(cxt, dowipe);
     830  	if (dowipe)
     831  		fdisk_warnx(cxt, _(
     832  			"The device contains '%s' signature and it will be removed by a write command. "
     833  			"See fdisk(8) man page and --wipe option for more details."),
     834  			fdisk_get_collision(cxt));
     835  	else
     836  		fdisk_warnx(cxt, _(
     837  			"The device contains '%s' signature and it may remain on the device. "
     838  			"It is recommended to wipe the device with wipefs(8) or "
     839  			"fdisk --wipe, in order to avoid possible collisions."),
     840  			fdisk_get_collision(cxt));
     841  }
     842  
     843  static void __attribute__((__noreturn__)) usage(void)
     844  {
     845  	FILE *out = stdout;
     846  
     847  	fputs(USAGE_HEADER, out);
     848  
     849  	fprintf(out,
     850  	      _(" %1$s [options] <disk>         change partition table\n"
     851  	        " %1$s [options] -l [<disk>...] list partition table(s)\n"),
     852  	       program_invocation_short_name);
     853  
     854  	fputs(USAGE_SEPARATOR, out);
     855  	fputs(_("Display or manipulate a disk partition table.\n"), out);
     856  
     857  	fputs(USAGE_OPTIONS, out);
     858  	fputs(_(" -b, --sector-size <size>      physical and logical sector size\n"), out);
     859  	fputs(_(" -B, --protect-boot            don't erase bootbits when creating a new label\n"), out);
     860  	fputs(_(" -c, --compatibility[=<mode>]  mode is 'dos' or 'nondos' (default)\n"), out);
     861  	fprintf(out,
     862  	      _(" -L, --color[=<when>]          colorize output (%s, %s or %s)\n"), "auto", "always", "never");
     863  	fprintf(out,
     864  	        "                                 %s\n", USAGE_COLORS_DEFAULT);
     865  	fputs(_(" -l, --list                    display partitions and exit\n"), out);
     866  	fputs(_(" -x, --list-details            like --list but with more details\n"), out);
     867  
     868  	fputs(_(" -n, --noauto-pt               don't create default partition table on empty devices\n"), out);
     869  	fputs(_(" -o, --output <list>           output columns\n"), out);
     870  	fputs(_(" -t, --type <type>             recognize specified partition table type only\n"), out);
     871  	fputs(_(" -u, --units[=<unit>]          display units: 'cylinders' or 'sectors' (default)\n"), out);
     872  	fputs(_(" -s, --getsz                   display device size in 512-byte sectors [DEPRECATED]\n"), out);
     873  	fputs(_("     --bytes                   print SIZE in bytes rather than in human readable format\n"), out);
     874  	fprintf(out,
     875  	      _("     --lock[=<mode>]           use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
     876  	fprintf(out,
     877  	      _(" -w, --wipe <mode>             wipe signatures (%s, %s or %s)\n"), "auto", "always", "never");
     878  	fprintf(out,
     879  	      _(" -W, --wipe-partitions <mode>  wipe signatures from new partitions (%s, %s or %s)\n"), "auto", "always", "never");
     880  
     881  	fputs(USAGE_SEPARATOR, out);
     882  	fputs(_(" -C, --cylinders <number>      specify the number of cylinders\n"), out);
     883  	fputs(_(" -H, --heads <number>          specify the number of heads\n"), out);
     884  	fputs(_(" -S, --sectors <number>        specify the number of sectors per track\n"), out);
     885  
     886  	fputs(USAGE_SEPARATOR, out);
     887  	printf(USAGE_HELP_OPTIONS(31));
     888  
     889  	list_available_columns(out);
     890  
     891  	printf(USAGE_MAN_TAIL("fdisk(8)"));
     892  	exit(EXIT_SUCCESS);
     893  }
     894  
     895  
     896  enum {
     897  	ACT_FDISK = 0,	/* default */
     898  	ACT_LIST,
     899  	ACT_LIST_DETAILS,
     900  	ACT_SHOWSIZE
     901  };
     902  
     903  int main(int argc, char **argv)
     904  {
     905  	int rc, i, c, act = ACT_FDISK, noauto_pt = 0;
     906  	int colormode = UL_COLORMODE_UNDEF;
     907  	struct fdisk_context *cxt;
     908  	char *outarg = NULL;
     909  	const char *devname, *lockmode = NULL;
     910  	enum {
     911  		OPT_BYTES	= CHAR_MAX + 1,
     912  		OPT_LOCK
     913  	};
     914  	static const struct option longopts[] = {
     915  		{ "bytes",          no_argument,       NULL, OPT_BYTES },
     916  		{ "color",          optional_argument, NULL, 'L' },
     917  		{ "compatibility",  optional_argument, NULL, 'c' },
     918  		{ "cylinders",      required_argument, NULL, 'C' },
     919  		{ "heads",          required_argument, NULL, 'H' },
     920  		{ "sectors",        required_argument, NULL, 'S' },
     921  		{ "getsz",          no_argument,       NULL, 's' },
     922  		{ "help",           no_argument,       NULL, 'h' },
     923  		{ "list",           no_argument,       NULL, 'l' },
     924  		{ "list-details",   no_argument,       NULL, 'x' },
     925  		{ "lock",           optional_argument, NULL, OPT_LOCK },
     926  		{ "noauto-pt",      no_argument,       NULL, 'n' },
     927  		{ "sector-size",    required_argument, NULL, 'b' },
     928  		{ "type",           required_argument, NULL, 't' },
     929  		{ "units",          optional_argument, NULL, 'u' },
     930  		{ "version",        no_argument,       NULL, 'V' },
     931  		{ "output",         required_argument, NULL, 'o' },
     932  		{ "protect-boot",   no_argument,       NULL, 'B' },
     933  		{ "wipe",           required_argument, NULL, 'w' },
     934  		{ "wipe-partitions",required_argument, NULL, 'W' },
     935  		{ NULL, 0, NULL, 0 }
     936  	};
     937  
     938  	setlocale(LC_ALL, "");
     939  	bindtextdomain(PACKAGE, LOCALEDIR);
     940  	textdomain(PACKAGE);
     941  	close_stdout_atexit();
     942  
     943  	fdisk_init_debug(0);
     944  	scols_init_debug(0);
     945  	fdiskprog_init_debug();
     946  
     947  	cxt = fdisk_new_context();
     948  	if (!cxt)
     949  		err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
     950  
     951  	fdisk_set_ask(cxt, ask_callback, NULL);
     952  
     953  	while ((c = getopt_long(argc, argv, "b:Bc::C:hH:lL::no:sS:t:u::vVw:W:x",
     954  				longopts, NULL)) != -1) {
     955  		switch (c) {
     956  		case 'b':
     957  		{
     958  			size_t sz = strtou32_or_err(optarg,
     959  					_("invalid sector size argument"));
     960  			if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096)
     961  				errx(EXIT_FAILURE, _("invalid sector size argument"));
     962  			fdisk_save_user_sector_size(cxt, sz, sz);
     963  			break;
     964  		}
     965  		case 'B':
     966  			fdisk_enable_bootbits_protection(cxt, 1);
     967  			break;
     968  		case 'C':
     969  			fdisk_save_user_geometry(cxt,
     970  				strtou32_or_err(optarg,
     971  						_("invalid cylinders argument")),
     972  				0, 0);
     973  			break;
     974  		case 'c':
     975  			if (optarg) {
     976  				/* this setting is independent on the current
     977  				 * actively used label
     978  				 */
     979  				char *p = *optarg == '=' ? optarg + 1 : optarg;
     980  				struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
     981  
     982  				if (!lb)
     983  					err(EXIT_FAILURE, _("not found DOS label driver"));
     984  				if (strcmp(p, "dos") == 0)
     985  					fdisk_dos_enable_compatible(lb, TRUE);
     986  				else if (strcmp(p, "nondos") == 0)
     987  					fdisk_dos_enable_compatible(lb, FALSE);
     988  				else
     989  					errx(EXIT_FAILURE, _("unknown compatibility mode '%s'"), p);
     990  			}
     991  			/* use default if no optarg specified */
     992  			break;
     993  		case 'H':
     994  			fdisk_save_user_geometry(cxt, 0,
     995  				strtou32_or_err(optarg,
     996  						_("invalid heads argument")),
     997  				0);
     998  			break;
     999  		case 'S':
    1000  			fdisk_save_user_geometry(cxt, 0, 0,
    1001  				strtou32_or_err(optarg,
    1002  					_("invalid sectors argument")));
    1003  			break;
    1004  		case 'l':
    1005  			act = ACT_LIST;
    1006  			break;
    1007  		case 'x':
    1008  			act = ACT_LIST_DETAILS;
    1009  			break;
    1010  		case 'L':
    1011  			colormode = UL_COLORMODE_AUTO;
    1012  			if (optarg)
    1013  				colormode = colormode_or_err(optarg,
    1014  						_("unsupported color mode"));
    1015  			break;
    1016  		case 'n':
    1017  			noauto_pt = 1;
    1018  			break;
    1019  		case 'o':
    1020  			outarg = optarg;
    1021  			break;
    1022  		case 's':
    1023  			act = ACT_SHOWSIZE;
    1024  			break;
    1025  		case 't':
    1026  		{
    1027  			struct fdisk_label *lb = NULL;
    1028  
    1029  			while (fdisk_next_label(cxt, &lb) == 0)
    1030  				fdisk_label_set_disabled(lb, 1);
    1031  
    1032  			lb = fdisk_get_label(cxt, optarg);
    1033  			if (!lb)
    1034  				errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg);
    1035  			fdisk_label_set_disabled(lb, 0);
    1036  			break;
    1037  		}
    1038  		case 'u':
    1039  			if (optarg && *optarg == '=')
    1040  				optarg++;
    1041  			if (fdisk_set_unit(cxt, optarg) != 0)
    1042  				errx(EXIT_FAILURE, _("unsupported unit"));
    1043  			break;
    1044  		case 'V': /* preferred for util-linux */
    1045  		case 'v': /* for backward compatibility only */
    1046  			print_version(EXIT_SUCCESS);
    1047  		case 'w':
    1048  			wipemode = wipemode_from_string(optarg);
    1049  			if (wipemode < 0)
    1050  				errx(EXIT_FAILURE, _("unsupported wipe mode"));
    1051  			break;
    1052  		case 'W':
    1053  			pwipemode = wipemode_from_string(optarg);
    1054  			if (pwipemode < 0)
    1055  				errx(EXIT_FAILURE, _("unsupported wipe mode"));
    1056  			break;
    1057  		case 'h':
    1058  			usage();
    1059  		case OPT_BYTES:
    1060  			fdisk_set_size_unit(cxt, FDISK_SIZEUNIT_BYTES);
    1061  			break;
    1062  		case OPT_LOCK:
    1063  			lockmode = "1";
    1064  			if (optarg) {
    1065  				if (*optarg == '=')
    1066  					optarg++;
    1067  				lockmode = optarg;
    1068  			}
    1069  			break;
    1070  		default:
    1071  			errtryhelp(EXIT_FAILURE);
    1072  		}
    1073  	}
    1074  
    1075  	if (argc-optind != 1 && fdisk_has_user_device_properties(cxt))
    1076  		warnx(_("The device properties (sector size and geometry) should"
    1077  			" be used with one specified device only."));
    1078  
    1079  	colors_init(colormode, "fdisk");
    1080  	is_interactive = isatty(STDIN_FILENO);
    1081  
    1082  	switch (act) {
    1083  	case ACT_LIST:
    1084  	case ACT_LIST_DETAILS:
    1085  		fdisk_enable_listonly(cxt, 1);
    1086  
    1087  		if (act == ACT_LIST_DETAILS)
    1088  			fdisk_enable_details(cxt, 1);
    1089  
    1090  		init_fields(cxt, outarg, NULL);
    1091  
    1092  		if (argc > optind) {
    1093  			int k;
    1094  
    1095  			for (rc = 0, k = optind; k < argc; k++)
    1096  				rc += print_device_pt(cxt, argv[k], 1, 0, k != optind);
    1097  
    1098  			if (rc)
    1099  				return EXIT_FAILURE;
    1100  		} else
    1101  			print_all_devices_pt(cxt, 0);
    1102  		break;
    1103  
    1104  	case ACT_SHOWSIZE:
    1105  		/* deprecated */
    1106  		if (argc - optind <= 0) {
    1107  			warnx(_("bad usage"));
    1108  			errtryhelp(EXIT_FAILURE);
    1109  		}
    1110  		for (i = optind; i < argc; i++) {
    1111  			uintmax_t blks = get_dev_blocks(argv[i]);
    1112  
    1113  			if (argc - optind == 1)
    1114  				printf("%ju\n", blks);
    1115  			else
    1116  				printf("%s: %ju\n", argv[i], blks);
    1117  		}
    1118  		break;
    1119  
    1120  	case ACT_FDISK:
    1121  		if (argc-optind != 1) {
    1122  			warnx(_("bad usage"));
    1123  			errtryhelp(EXIT_FAILURE);
    1124  		}
    1125  
    1126  		/* Here starts interactive mode, use fdisk_{warn,info,..} functions */
    1127  		color_scheme_enable("welcome", UL_COLOR_GREEN);
    1128  		fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING);
    1129  		color_disable();
    1130  		fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n"
    1131  				  "Be careful before using the write command.\n"));
    1132  
    1133  		devname = argv[optind];
    1134  		rc = fdisk_assign_device(cxt, devname, 0);
    1135  		if (rc == -EACCES) {
    1136  			rc = fdisk_assign_device(cxt, devname, 1);
    1137  			if (rc == 0)
    1138  				fdisk_warnx(cxt, _("Device is open in read-only mode."));
    1139  		}
    1140  		if (rc)
    1141  			err(EXIT_FAILURE, _("cannot open %s"), devname);
    1142  
    1143  		if (fdisk_device_is_used(cxt))
    1144  			fdisk_warnx(cxt, _(
    1145  			"This disk is currently in use - repartitioning is probably a bad idea.\n"
    1146  			"It's recommended to umount all file systems, and swapoff all swap\n"
    1147  			"partitions on this disk.\n"));
    1148  
    1149  		fflush(stdout);
    1150  
    1151  		if (!fdisk_is_readonly(cxt)
    1152  		    && blkdev_lock(fdisk_get_devfd(cxt), devname, lockmode) != 0) {
    1153  			fdisk_deassign_device(cxt, 1);
    1154  			fdisk_unref_context(cxt);
    1155  			return EXIT_FAILURE;
    1156  		}
    1157  
    1158  		if (fdisk_get_collision(cxt))
    1159  			follow_wipe_mode(cxt);
    1160  
    1161  		if (!fdisk_has_label(cxt)) {
    1162  			fdisk_info(cxt, _("Device does not contain a recognized partition table."));
    1163  			if (!noauto_pt)
    1164  				fdisk_create_disklabel(cxt, NULL);
    1165  
    1166  		} else if (fdisk_is_label(cxt, GPT) && fdisk_gpt_is_hybrid(cxt))
    1167  			fdisk_warnx(cxt, _(
    1168  				  "A hybrid GPT was detected. You have to sync "
    1169  				  "the hybrid MBR manually (expert command 'M')."));
    1170  
    1171  		init_fields(cxt, outarg, NULL);		/* -o <columns> */
    1172  
    1173  		if (!fdisk_is_readonly(cxt)) {
    1174  			fdisk_get_partitions(cxt, &original_layout);
    1175  			device_is_used = fdisk_device_is_used(cxt);
    1176  		}
    1177  
    1178  		while (1)
    1179  			process_fdisk_menu(&cxt);
    1180  	}
    1181  
    1182  	fdisk_unref_context(cxt);
    1183  	return EXIT_SUCCESS;
    1184  }