]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/fdisk.c
misc: never use usage(stderr)
[thirdparty/util-linux.git] / disk-utils / fdisk.c
CommitLineData
3f0ac587 1/*
6dbe3af9 2 * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
38b36353 3 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
6dbe3af9 4 *
3f0ac587
KZ
5 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
6 *
6dbe3af9
KZ
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.
6dbe3af9 11 */
6dbe3af9
KZ
12#include <unistd.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <fcntl.h>
17#include <ctype.h>
6dbe3af9 18#include <errno.h>
2b6fc908 19#include <getopt.h>
2b6fc908 20#include <sys/stat.h>
f26873dc 21#include <sys/time.h>
bb6aacfe 22#include <time.h>
ee5355e0 23#include <limits.h>
d44115f3 24#include <libsmartcols.h>
740c36f6 25#ifdef HAVE_LIBREADLINE
57ebfde2 26# define _FUNCTION_DEF
740c36f6
KZ
27# include <readline/readline.h>
28#endif
6dbe3af9 29
3f0ac587 30#include "c.h"
52b38677 31#include "xalloc.h"
e916600f 32#include "all-io.h"
22853e4a 33#include "nls.h"
e66ac5d3 34#include "rpmatch.h"
810f986b 35#include "blkdev.h"
b8d22034 36#include "mbsalign.h"
6bec8710
KZ
37#include "pathnames.h"
38#include "canonicalize.h"
20aa2570 39#include "strutils.h"
b2d28533 40#include "closestream.h"
8d2f4498 41#include "pager.h"
5c36a0eb 42
fdb006e8
KZ
43#include "fdisk.h"
44
b4bfbadd 45#include "pt-sun.h" /* to toggle flags */
5c36a0eb 46
48d7b13a 47#ifdef HAVE_LINUX_COMPILER_H
3f0ac587 48# include <linux/compiler.h>
48d7b13a
KZ
49#endif
50#ifdef HAVE_LINUX_BLKPG_H
3f0ac587 51# include <linux/blkpg.h>
7eda085c 52#endif
6dbe3af9 53
ba465623 54int pwipemode = WIPEMODE_AUTO;
5635d195 55static int wipemode = WIPEMODE_AUTO;
ba465623 56
e6d0c4c1
KZ
57/*
58 * fdisk debug stuff (see fdisk.h and include/debug.h)
59 */
60UL_DEBUG_DEFINE_MASK(fdisk);
819d9a29 61UL_DEBUG_DEFINE_MASKNAMES(fdisk) = UL_DEBUG_EMPTY_MASKNAMES;
d6b3ba41 62
e6d0c4c1
KZ
63static void fdiskprog_init_debug(void)
64{
65 __UL_INIT_DEBUG(fdisk, FDISKPROG_DEBUG_, 0, FDISK_DEBUG);
66}
d6b3ba41 67
740c36f6
KZ
68#ifdef HAVE_LIBREADLINE
69static char *rl_fgets(char *s, int n, FILE *stream, const char *prompt)
70{
71 char *p;
72
73 rl_outstream = stream;
74 p = readline(prompt);
75 if (!p)
76 return NULL;
77
84ab260a
KZ
78 strncpy(s, p, n);
79 s[n - 1] = '\0';
740c36f6
KZ
80 free(p);
81 return s;
82}
83#endif
84
d6b3ba41
KZ
85int get_user_reply(struct fdisk_context *cxt, const char *prompt,
86 char *buf, size_t bufsz)
87{
88 char *p;
89 size_t sz;
90
91 do {
740c36f6
KZ
92#ifdef HAVE_LIBREADLINE
93 if (isatty(STDIN_FILENO)) {
94 if (!rl_fgets(buf, bufsz, stdout, prompt)) {
95 if (fdisk_label_is_changed(fdisk_get_label(cxt, NULL))) {
96 if (rl_fgets(buf, bufsz, stderr,
97 _("\nDo you really want to quit? "))
98 && !rpmatch(buf))
99 continue;
100 }
101 fdisk_unref_context(cxt);
102 exit(EXIT_FAILURE);
103 } else
104 break;
105 }
106 else
107#endif
108 {
109 fputs(prompt, stdout);
110 fflush(stdout);
111 if (!fgets(buf, bufsz, stdin)) {
112 if (fdisk_label_is_changed(fdisk_get_label(cxt, NULL))) {
113 fprintf(stderr, _("\nDo you really want to quit? "));
d6b3ba41 114
740c36f6
KZ
115 if (fgets(buf, bufsz, stdin) && !rpmatch(buf))
116 continue;
117 }
118 fdisk_unref_context(cxt);
119 exit(EXIT_FAILURE);
120 } else
121 break;
122 }
d6b3ba41
KZ
123 } while (1);
124
125 for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */
126
127 if (p > buf)
128 memmove(buf, p, p - buf); /* remove blank space */
129 sz = strlen(buf);
130 if (sz && *(buf + sz - 1) == '\n')
131 *(buf + sz - 1) = '\0';
132
88141067 133 DBG(ASK, ul_debug("user's reply: >>>%s<<<", buf));
d6b3ba41
KZ
134 return 0;
135}
136
137static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask,
138 char *buf, size_t bufsz)
139
140{
141 const char *q = fdisk_ask_get_query(ask);
142 int dft = fdisk_ask_menu_get_default(ask);
143
144 if (q) {
145 fputs(q, stdout); /* print header */
146 fputc('\n', stdout);
147 }
148
149 do {
150 char prompt[128];
68ddb136 151 int key, c, rc;
d6b3ba41
KZ
152 const char *name, *desc;
153 size_t i = 0;
154
155 /* print menu items */
156 while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0)
157 fprintf(stdout, " %c %s (%s)\n", key, name, desc);
158
159 /* ask for key */
160 snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft);
68ddb136
KZ
161 rc = get_user_reply(cxt, prompt, buf, bufsz);
162 if (rc)
163 return rc;
d6b3ba41
KZ
164 if (!*buf) {
165 fdisk_info(cxt, _("Using default response %c."), dft);
166 c = dft;
167 } else
168 c = tolower(buf[0]);
169
170 /* check result */
171 i = 0;
172 while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) {
173 if (c == key) {
174 fdisk_ask_menu_set_result(ask, c);
175 return 0; /* success */
176 }
177 }
178 fdisk_warnx(cxt, _("Value out of range."));
179 } while (1);
180
181 return -EINVAL;
182}
183
184
185#define tochar(num) ((int) ('a' + num - 1))
186static int ask_number(struct fdisk_context *cxt,
187 struct fdisk_ask *ask,
188 char *buf, size_t bufsz)
189{
190 char prompt[128] = { '\0' };
191 const char *q = fdisk_ask_get_query(ask);
192 const char *range = fdisk_ask_number_get_range(ask);
193
194 uint64_t dflt = fdisk_ask_number_get_default(ask),
195 low = fdisk_ask_number_get_low(ask),
196 high = fdisk_ask_number_get_high(ask);
197 int inchar = fdisk_ask_number_inchars(ask);
198
199 assert(q);
200
88141067 201 DBG(ASK, ul_debug("asking for number "
fdbd7bb9 202 "['%s', <%"PRIu64",%"PRIu64">, default=%"PRIu64", range: %s]",
d6b3ba41
KZ
203 q, low, high, dflt, range));
204
205 if (range && dflt >= low && dflt <= high) {
206 if (inchar)
207 snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "),
208 q, range, tochar(dflt));
209 else
fdbd7bb9 210 snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
d6b3ba41
KZ
211 q, range, dflt);
212
213 } else if (dflt >= low && dflt <= high) {
214 if (inchar)
215 snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "),
216 q, tochar(low), tochar(high), tochar(dflt));
217 else
fdbd7bb9
RM
218 snprintf(prompt, sizeof(prompt),
219 _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
d6b3ba41
KZ
220 q, low, high, dflt);
221 } else if (inchar)
222 snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "),
223 q, tochar(low), tochar(high));
224 else
fdbd7bb9 225 snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
d6b3ba41
KZ
226 q, low, high);
227
228 do {
229 int rc = get_user_reply(cxt, prompt, buf, bufsz);
230 uint64_t num;
231
232 if (rc)
233 return rc;
234 if (!*buf && dflt >= low && dflt <= high)
235 return fdisk_ask_number_set_result(ask, dflt);
236
237 if (isdigit_string(buf)) {
238 char *end;
239
240 errno = 0;
241 num = strtoumax(buf, &end, 10);
242 if (errno || buf == end || (end && *end))
243 continue;
244 } else if (inchar && isalpha(*buf)) {
245 num = tolower(*buf) - 'a' + 1;
246 } else
247 rc = -EINVAL;
248
249 if (rc == 0 && num >= low && num <= high)
250 return fdisk_ask_number_set_result(ask, num);
251
252 fdisk_warnx(cxt, _("Value out of range."));
253 } while (1);
254
255 return -1;
256}
257
258static int ask_offset(struct fdisk_context *cxt,
259 struct fdisk_ask *ask,
260 char *buf, size_t bufsz)
261{
262 char prompt[128] = { '\0' };
263 const char *q = fdisk_ask_get_query(ask);
264 const char *range = fdisk_ask_number_get_range(ask);
265
266 uint64_t dflt = fdisk_ask_number_get_default(ask),
267 low = fdisk_ask_number_get_low(ask),
268 high = fdisk_ask_number_get_high(ask),
269 base = fdisk_ask_number_get_base(ask);
270
271 assert(q);
272
fdbd7bb9 273 DBG(ASK, ul_debug("asking for offset ['%s', <%"PRIu64",%"PRIu64">, base=%"PRIu64", default=%"PRIu64", range: %s]",
d6b3ba41
KZ
274 q, low, high, base, dflt, range));
275
276 if (range && dflt >= low && dflt <= high)
fdbd7bb9
RM
277 snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
278 q, range, dflt);
d6b3ba41 279 else if (dflt >= low && dflt <= high)
fdbd7bb9
RM
280 snprintf(prompt, sizeof(prompt),
281 _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
282 q, low, high, dflt);
d6b3ba41 283 else
fdbd7bb9
RM
284 snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
285 q, low, high);
d6b3ba41
KZ
286
287 do {
262c94c2 288 uintmax_t num = 0;
d6b3ba41
KZ
289 char sig = 0, *p;
290 int pwr = 0;
291
292 int rc = get_user_reply(cxt, prompt, buf, bufsz);
293 if (rc)
294 return rc;
295 if (!*buf && dflt >= low && dflt <= high)
296 return fdisk_ask_number_set_result(ask, dflt);
297
298 p = buf;
299 if (*p == '+' || *p == '-') {
300 sig = *buf;
301 p++;
302 }
303
304 rc = parse_size(p, &num, &pwr);
305 if (rc)
306 continue;
88141067 307 DBG(ASK, ul_debug("parsed size: %ju", num));
d6b3ba41
KZ
308 if (sig && pwr) {
309 /* +{size}{K,M,...} specified, the "num" is in bytes */
310 uint64_t unit = fdisk_ask_number_get_unit(ask);
311 num += unit/2; /* round */
312 num /= unit;
313 }
314 if (sig == '+')
315 num += base;
316 else if (sig == '-')
317 num = base - num;
318
88141067 319 DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]",
d6b3ba41
KZ
320 num, sig, pwr,
321 sig ? "relative" : "absolute"));
322 if (num >= low && num <= high) {
8c73e509 323 if (sig && pwr)
d6b3ba41 324 fdisk_ask_number_set_relative(ask, 1);
262c94c2 325 return fdisk_ask_number_set_result(ask, (uint64_t)num);
d6b3ba41
KZ
326 }
327 fdisk_warnx(cxt, _("Value out of range."));
328 } while (1);
329
330 return -1;
331}
332
333static unsigned int info_count;
334
412791a9 335static void fputs_info(struct fdisk_ask *ask, FILE *out)
d6b3ba41
KZ
336{
337 const char *msg;
d6b3ba41
KZ
338 assert(ask);
339
340 msg = fdisk_ask_print_get_mesg(ask);
d6b3ba41
KZ
341 if (!msg)
342 return;
343 if (info_count == 1)
344 fputc('\n', out);
412791a9
KZ
345
346 fputs(msg, out);
d6b3ba41
KZ
347 fputc('\n', out);
348}
349
350int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
351 void *data __attribute__((__unused__)))
352{
353 int rc = 0;
354 char buf[BUFSIZ];
355
356 assert(cxt);
357 assert(ask);
358
359 if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO)
360 info_count = 0;
361
362 switch(fdisk_ask_get_type(ask)) {
363 case FDISK_ASKTYPE_MENU:
364 return ask_menu(cxt, ask, buf, sizeof(buf));
365 case FDISK_ASKTYPE_NUMBER:
366 return ask_number(cxt, ask, buf, sizeof(buf));
367 case FDISK_ASKTYPE_OFFSET:
368 return ask_offset(cxt, ask, buf, sizeof(buf));
369 case FDISK_ASKTYPE_INFO:
152788aa
KZ
370 if (!fdisk_is_listonly(cxt))
371 info_count++;
412791a9 372 fputs_info(ask, stdout);
d6b3ba41
KZ
373 break;
374 case FDISK_ASKTYPE_WARNX:
95aae4fc 375 fflush(stdout);
496c979a 376 color_scheme_fenable("warn", UL_COLOR_RED, stderr);
d6b3ba41
KZ
377 fputs(fdisk_ask_print_get_mesg(ask), stderr);
378 color_fdisable(stderr);
379 fputc('\n', stderr);
380 break;
381 case FDISK_ASKTYPE_WARN:
95aae4fc 382 fflush(stdout);
496c979a 383 color_scheme_fenable("warn", UL_COLOR_RED, stderr);
d6b3ba41
KZ
384 fputs(fdisk_ask_print_get_mesg(ask), stderr);
385 errno = fdisk_ask_print_get_errno(ask);
386 fprintf(stderr, ": %m\n");
387 color_fdisable(stderr);
388 break;
389 case FDISK_ASKTYPE_YESNO:
390 fputc('\n', stdout);
2d129032
KZ
391 do {
392 int x;
393 fputs(fdisk_ask_get_query(ask), stdout);
394 rc = get_user_reply(cxt, _(" [Y]es/[N]o: "), buf, sizeof(buf));
395 if (rc)
396 break;
397 x = rpmatch(buf);
cd2a6f1c 398 if (x == RPMATCH_YES || x == RPMATCH_NO) {
2d129032
KZ
399 fdisk_ask_yesno_set_result(ask, x);
400 break;
401 }
402 } while(1);
88141067 403 DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc));
d6b3ba41
KZ
404 break;
405 case FDISK_ASKTYPE_STRING:
406 {
407 char prmt[BUFSIZ];
408 snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask));
409 fputc('\n', stdout);
410 rc = get_user_reply(cxt, prmt, buf, sizeof(buf));
411 if (rc == 0)
412 fdisk_ask_string_set_result(ask, xstrdup(buf));
88141067 413 DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc));
d6b3ba41
KZ
414 break;
415 }
416 default:
417 warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
418 return -EINVAL;
419 }
420 return rc;
421}
422
4bd02cdf 423static struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt)
d6b3ba41
KZ
424{
425 const char *q;
a745611d
KZ
426 struct fdisk_label *lb;
427
428 assert(cxt);
429 lb = fdisk_get_label(cxt, NULL);
d6b3ba41 430
a745611d 431 if (!lb)
d6b3ba41
KZ
432 return NULL;
433
a745611d 434 q = fdisk_label_has_code_parttypes(lb) ?
84111dba
KZ
435 _("Hex code (type L to list all codes): ") :
436 _("Partition type (type L to list all types): ");
d6b3ba41
KZ
437 do {
438 char buf[256];
439 int rc = get_user_reply(cxt, q, buf, sizeof(buf));
440
441 if (rc)
442 break;
443
444 if (buf[1] == '\0' && toupper(*buf) == 'L')
445 list_partition_types(cxt);
446 else if (*buf)
a745611d 447 return fdisk_label_parse_parttype(lb, buf);
d6b3ba41
KZ
448 } while (1);
449
450 return NULL;
451}
452
8d2f4498 453
7b575fcc 454void list_partition_types(struct fdisk_context *cxt)
6dbe3af9 455{
76f17cf2 456 size_t ntypes = 0;
f45f5c83 457 struct fdisk_label *lb;
6dbe3af9 458
a745611d
KZ
459 assert(cxt);
460 lb = fdisk_get_label(cxt, NULL);
461 if (!lb)
462 return;
463 ntypes = fdisk_label_get_nparttypes(lb);
464 if (!ntypes)
7b575fcc 465 return;
2b6fc908 466
a745611d 467 if (fdisk_label_has_code_parttypes(lb)) {
7b575fcc
KZ
468 /*
469 * Prints in 4 columns in format <hex> <name>
470 */
76f17cf2
KZ
471 size_t last[4], done = 0, next = 0, size;
472 int i;
b8d22034 473
76f17cf2 474 size = ntypes;
7b575fcc
KZ
475
476 for (i = 3; i >= 0; i--)
477 last[3 - i] = done += (size + i - done) / (i + 1);
478 i = done = 0;
479
480 do {
481 #define NAME_WIDTH 15
482 char name[NAME_WIDTH * MB_LEN_MAX];
483 size_t width = NAME_WIDTH;
a745611d 484 const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, next);
7b575fcc
KZ
485 size_t ret;
486
e6d0c4c1 487 if (fdisk_parttype_get_name(t)) {
a745611d
KZ
488 printf("%c%2x ", i ? ' ' : '\n',
489 fdisk_parttype_get_code(t));
490 ret = mbsalign(_(fdisk_parttype_get_name(t)),
491 name, sizeof(name),
492 &width, MBS_ALIGN_LEFT, 0);
7b575fcc 493
76f17cf2 494 if (ret == (size_t)-1 || ret >= sizeof(name))
a745611d
KZ
495 printf("%-15.15s",
496 _(fdisk_parttype_get_name(t)));
76f17cf2
KZ
497 else
498 fputs(name, stdout);
499 }
7b575fcc
KZ
500
501 next = last[i++] + done;
502 if (i > 3 || next >= last[i]) {
503 i = 0;
504 next = ++done;
505 }
506 } while (done < last[0]);
507
508 } else {
509 /*
510 * Prints 1 column in format <idx> <name> <typestr>
511 */
76f17cf2 512 size_t i;
7b575fcc 513
8d2f4498
KZ
514 pager_open();
515
a745611d
KZ
516 for (i = 0; i < ntypes; i++) {
517 const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, i);
518 printf("%3zu %-30s %s\n", i + 1,
519 fdisk_parttype_get_name(t),
520 fdisk_parttype_get_string(t));
76f17cf2 521 }
8d2f4498
KZ
522
523 pager_close();
7b575fcc 524 }
6dbe3af9
KZ
525 putchar('\n');
526}
527
f02fecd1 528void toggle_dos_compatibility_flag(struct fdisk_context *cxt)
852ce62b 529{
6a632136 530 struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
852ce62b
KZ
531 int flag;
532
533 if (!lb)
534 return;
535
536 flag = !fdisk_dos_is_compatible(lb);
09f0c3d9
KZ
537 fdisk_info(cxt, flag ?
538 _("DOS Compatibility flag is set (DEPRECATED!)") :
539 _("DOS Compatibility flag is not set"));
edd7b958 540
852ce62b
KZ
541 fdisk_dos_enable_compatible(lb, flag);
542
aa36c2cf 543 if (fdisk_is_label(cxt, DOS))
09f0c3d9 544 fdisk_reset_alignment(cxt); /* reset the current label */
6dbe3af9
KZ
545}
546
27ddd4f1 547void change_partition_type(struct fdisk_context *cxt)
e53ced85 548{
641a75ca 549 size_t i;
85151521
KZ
550 struct fdisk_parttype *t = NULL;
551 struct fdisk_partition *pa = NULL;
552 const char *old = NULL;
6dbe3af9 553
e3661531 554 assert(cxt);
e3661531 555
641a75ca 556 if (fdisk_ask_partnum(cxt, &i, FALSE))
24f4bbff 557 return;
2b6fc908 558
85151521
KZ
559 if (fdisk_get_partition(cxt, i, &pa)) {
560 fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
561 return;
562 }
563
564 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
e6d0c4c1 565 old = t ? fdisk_parttype_get_name(t) : _("Unknown");
85151521
KZ
566
567 do {
568 t = ask_partition_type(cxt);
569 } while (!t);
570
85151521 571 if (fdisk_set_partition_type(cxt, i, t) == 0)
0477369a 572 fdisk_info(cxt,
85151521 573 _("Changed type of partition '%s' to '%s'."),
a745611d 574 old, t ? fdisk_parttype_get_name(t) : _("Unknown"));
85151521
KZ
575 else
576 fdisk_info(cxt,
577 _("Type of partition %zu is unchanged: %s."),
578 i + 1, old);
579
d6cfa8b3 580 fdisk_unref_partition(pa);
4bd02cdf 581 fdisk_unref_parttype(t);
6dbe3af9
KZ
582}
583
3e3b51b3
JLB
584int print_partition_info(struct fdisk_context *cxt)
585{
586 struct fdisk_partition *pa = NULL;
6360bcca 587 int rc = 0;
3e3b51b3
JLB
588 size_t i, nfields;
589 int *fields = NULL;
590 struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
591
592 if ((rc = fdisk_ask_partnum(cxt, &i, FALSE)))
593 return rc;
594
595 if ((rc = fdisk_get_partition(cxt, i, &pa))) {
596 fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
597 return rc;
598 }
599
6360bcca 600 if ((rc = fdisk_label_get_fields_ids_all(lb, cxt, &fields, &nfields)))
3e3b51b3 601 goto clean_data;
3e3b51b3
JLB
602
603 for (i = 0; i < nfields; ++i) {
604 int id = fields[i];
605 char *data = NULL;
606 const struct fdisk_field *fd = fdisk_label_get_field(lb, id);
607
608 if (!fd)
609 continue;
610
611 rc = fdisk_partition_to_string(pa, cxt, id, &data);
612 if (rc < 0)
613 goto clean_data;
6360bcca
KZ
614 if (!data || !*data)
615 continue;
3e3b51b3
JLB
616 fdisk_info(cxt, _("%15s: %s"), fdisk_field_get_name(fd), data);
617 free(data);
618 }
619
620clean_data:
621 fdisk_unref_partition(pa);
622 free(fields);
623 return rc;
624}
625
e916600f
KZ
626static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz)
627{
628 size_t next;
629 const unsigned char *p0 = buf + i;
630
631 for (next = i + 16; next < sz; next += 16) {
632 if (memcmp(p0, buf + next, 16) != 0)
633 break;
634 }
635
636 return next == i + 16 ? i : next;
637}
6dbe3af9 638
e916600f 639static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all)
09f0c3d9 640{
e916600f
KZ
641 size_t i, l, next = 0;
642
643 if (!buf)
644 return;
645 for (i = 0, l = 0; i < sz; i++, l++) {
646 if (l == 0) {
647 if (all == 0 && !next)
648 next = skip_empty(buf, i, sz);
fdbd7bb9 649 printf("%08jx ", (intmax_t)base + i);
e916600f
KZ
650 }
651 printf(" %02x", buf[i]);
652 if (l == 7) /* words separator */
653 fputs(" ", stdout);
654 else if (l == 15) {
655 fputc('\n', stdout); /* next line */
6dbe3af9 656 l = -1;
e916600f
KZ
657 if (next > i) {
658 printf("*\n");
659 i = next - 1;
660 }
661 next = 0;
6dbe3af9
KZ
662 }
663 }
664 if (l > 0)
665 printf("\n");
6dbe3af9
KZ
666}
667
e916600f 668static void dump_blkdev(struct fdisk_context *cxt, const char *name,
9bbcf43f 669 uint64_t offset, size_t size, int all)
e53ced85 670{
8eccde20
KZ
671 int fd = fdisk_get_devfd(cxt);
672
fdbd7bb9 673 fdisk_info(cxt, _("\n%s: offset = %"PRIu64", size = %zu bytes."),
e916600f
KZ
674 name, offset, size);
675
8eccde20
KZ
676 assert(fd >= 0);
677
9bbcf43f 678 if (lseek(fd, (off_t) offset, SEEK_SET) == (off_t) -1)
e916600f 679 fdisk_warn(cxt, _("cannot seek"));
5afed187
KZ
680 else {
681 unsigned char *buf = xmalloc(size);
682
8eccde20 683 if (read_all(fd, (char *) buf, size) != (ssize_t) size)
5afed187
KZ
684 fdisk_warn(cxt, _("cannot read"));
685 else
686 dump_buffer(offset, buf, size, all);
687 free(buf);
688 }
e916600f
KZ
689}
690
691void dump_firstsector(struct fdisk_context *cxt)
692{
693 int all = !isatty(STDOUT_FILENO);
694
e3661531 695 assert(cxt);
6dbe3af9 696
8eccde20 697 dump_blkdev(cxt, _("First sector"), 0, fdisk_get_sector_size(cxt), all);
e916600f
KZ
698}
699
700void dump_disklabel(struct fdisk_context *cxt)
701{
702 int all = !isatty(STDOUT_FILENO);
703 int i = 0;
704 const char *name = NULL;
9bbcf43f 705 uint64_t offset = 0;
e916600f
KZ
706 size_t size = 0;
707
708 assert(cxt);
e3661531 709
e916600f
KZ
710 while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size)
711 dump_blkdev(cxt, name, offset, size, all);
6dbe3af9
KZ
712}
713
0073a4cf 714static fdisk_sector_t get_dev_blocks(char *dev)
6da365de 715{
bbe67996 716 int fd, ret;
0073a4cf 717 fdisk_sector_t size;
6da365de
DB
718
719 if ((fd = open(dev, O_RDONLY)) < 0)
289dcc90 720 err(EXIT_FAILURE, _("cannot open %s"), dev);
0073a4cf 721 ret = blkdev_get_sectors(fd, (unsigned long long *) &size);
6da365de 722 close(fd);
bbe67996
SK
723 if (ret < 0)
724 err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
6da365de
DB
725 return size/2;
726}
727
5635d195
KZ
728
729void follow_wipe_mode(struct fdisk_context *cxt)
730{
731 int dowipe = wipemode == WIPEMODE_ALWAYS ? 1 : 0;
732
733 if (isatty(STDIN_FILENO) && wipemode == WIPEMODE_AUTO)
734 dowipe = 1; /* do it in interactive mode */
735
736 if (fdisk_is_ptcollision(cxt) && wipemode != WIPEMODE_NEVER)
737 dowipe = 1; /* always remove old PT */
738
739 fdisk_enable_wipe(cxt, dowipe);
740 if (dowipe)
741 fdisk_warnx(cxt, _(
742 "The old %s signature will be removed by a write command."),
743 fdisk_get_collision(cxt));
744 else
745 fdisk_warnx(cxt, _(
746 "The old %s signature may remain on the device. "
747 "It is recommended to wipe the device with wipefs(8) or "
748 "fdisk --wipe, in order to avoid possible collisions."),
749 fdisk_get_collision(cxt));
750}
751
6e1eda6f 752static void __attribute__((__noreturn__)) usage(void)
e3a4aaa7 753{
6e1eda6f
RM
754 FILE *out = stdout;
755
e3a4aaa7
KZ
756 fputs(USAGE_HEADER, out);
757
758 fprintf(out,
759 _(" %1$s [options] <disk> change partition table\n"
760 " %1$s [options] -l [<disk>] list partition table(s)\n"),
761 program_invocation_short_name);
762
451dbcfa
BS
763 fputs(USAGE_SEPARATOR, out);
764 fputs(_("Display or manipulate a disk partition table.\n"), out);
765
e3a4aaa7
KZ
766 fputs(USAGE_OPTIONS, out);
767 fputs(_(" -b, --sector-size <size> physical and logical sector size\n"), out);
718b6f0c 768 fputs(_(" -B, --protect-boot don't erase bootbits when creating a new label\n"), out);
e3a4aaa7
KZ
769 fputs(_(" -c, --compatibility[=<mode>] mode is 'dos' or 'nondos' (default)\n"), out);
770 fputs(_(" -L, --color[=<when>] colorize output (auto, always or never)\n"), out);
5d51dc2a
KZ
771 fprintf(out,
772 " %s\n", USAGE_COLORS_DEFAULT);
9e930041 773 fputs(_(" -l, --list display partitions and exit\n"), out);
2d7dffeb 774 fputs(_(" -o, --output <list> output columns\n"), out);
e3a4aaa7
KZ
775 fputs(_(" -t, --type <type> recognize specified partition table type only\n"), out);
776 fputs(_(" -u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out);
777 fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out);
354f8cc8 778 fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out);
cb9a4b00 779 fputs(_(" -w, --wipe <mode> wipe signatures (auto, always or never)\n"), out);
ba465623 780 fputs(_(" -W, --wipe-partitions <mode> wipe signatures from new partitions (auto, always or never)\n"), out);
e3a4aaa7
KZ
781
782 fputs(USAGE_SEPARATOR, out);
4b4e391a
KZ
783 fputs(_(" -C, --cylinders <number> specify the number of cylinders\n"), out);
784 fputs(_(" -H, --heads <number> specify the number of heads\n"), out);
785 fputs(_(" -S, --sectors <number> specify the number of sectors per track\n"), out);
e3a4aaa7
KZ
786
787 fputs(USAGE_SEPARATOR, out);
788 fputs(USAGE_HELP, out);
789 fputs(USAGE_VERSION, out);
790
fff8ad58
KZ
791 list_available_columns(out);
792
e3a4aaa7 793 fprintf(out, USAGE_MAN_TAIL("fdisk(8)"));
6e1eda6f 794 exit(EXIT_SUCCESS);
e3a4aaa7
KZ
795}
796
797
5b72b3dd
KZ
798enum {
799 ACT_FDISK = 0, /* default */
800 ACT_LIST,
801 ACT_SHOWSIZE
802};
803
e1144767
DB
804int main(int argc, char **argv)
805{
e146ae4e 806 int rc, i, c, act = ACT_FDISK;
d0c9ddc3 807 int colormode = UL_COLORMODE_UNDEF;
4e0e8253 808 struct fdisk_context *cxt;
fff8ad58 809 char *outarg = NULL;
354f8cc8
KZ
810 enum {
811 OPT_BYTES = CHAR_MAX + 1
812 };
e3a4aaa7 813 static const struct option longopts[] = {
354f8cc8 814 { "bytes", no_argument, NULL, OPT_BYTES },
e3a4aaa7
KZ
815 { "color", optional_argument, NULL, 'L' },
816 { "compatibility", optional_argument, NULL, 'c' },
4b4e391a
KZ
817 { "cylinders", required_argument, NULL, 'C' },
818 { "heads", required_argument, NULL, 'H' },
819 { "sectors", required_argument, NULL, 'S' },
e3a4aaa7
KZ
820 { "getsz", no_argument, NULL, 's' },
821 { "help", no_argument, NULL, 'h' },
822 { "list", no_argument, NULL, 'l' },
823 { "sector-size", required_argument, NULL, 'b' },
824 { "type", required_argument, NULL, 't' },
825 { "units", optional_argument, NULL, 'u' },
826 { "version", no_argument, NULL, 'V' },
2d7dffeb 827 { "output", no_argument, NULL, 'o' },
aeb9a30b 828 { "protect-boot", no_argument, NULL, 'B' },
cb9a4b00 829 { "wipe", required_argument, NULL, 'w' },
ba465623 830 { "wipe-partitions",required_argument, NULL, 'W' },
e3a4aaa7
KZ
831 { NULL, 0, NULL, 0 }
832 };
833
7eda085c
KZ
834 setlocale(LC_ALL, "");
835 bindtextdomain(PACKAGE, LOCALEDIR);
836 textdomain(PACKAGE);
b2d28533 837 atexit(close_stdout);
2b6fc908 838
4e0e8253 839 fdisk_init_debug(0);
053939a4 840 scols_init_debug(0);
e6d0c4c1
KZ
841 fdiskprog_init_debug();
842
4e0e8253
KZ
843 cxt = fdisk_new_context();
844 if (!cxt)
845 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
846
6a632136 847 fdisk_set_ask(cxt, ask_callback, NULL);
416c43a9 848
ba465623 849 while ((c = getopt_long(argc, argv, "b:Bc::C:hH:lL::o:sS:t:u::vVw:W:",
e3a4aaa7 850 longopts, NULL)) != -1) {
2b6fc908
KZ
851 switch (c) {
852 case 'b':
6bcd192c
KZ
853 {
854 size_t sz = strtou32_or_err(optarg,
855 _("invalid sector size argument"));
856 if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096)
6e1eda6f 857 errx(EXIT_FAILURE, _("invalid sector size argument"));
6bcd192c 858 fdisk_save_user_sector_size(cxt, sz, sz);
2b6fc908 859 break;
6bcd192c 860 }
9ff89c5d 861 case 'B':
aeb9a30b
KZ
862 fdisk_enable_bootbits_protection(cxt, 1);
863 break;
0e6f4a20 864 case 'C':
1653f0b0
KZ
865 fdisk_save_user_geometry(cxt,
866 strtou32_or_err(optarg,
867 _("invalid cylinders argument")),
868 0, 0);
0e6f4a20 869 break;
78498b7b 870 case 'c':
852ce62b 871 if (optarg) {
2b0bc17b 872 /* this setting is independent on the current
e3a4aaa7
KZ
873 * actively used label
874 */
875 char *p = *optarg == '=' ? optarg + 1 : optarg;
6a632136 876 struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
e3a4aaa7 877
852ce62b
KZ
878 if (!lb)
879 err(EXIT_FAILURE, _("not found DOS label driver"));
e3a4aaa7 880 if (strcmp(p, "dos") == 0)
852ce62b 881 fdisk_dos_enable_compatible(lb, TRUE);
e3a4aaa7 882 else if (strcmp(p, "nondos") == 0)
852ce62b 883 fdisk_dos_enable_compatible(lb, FALSE);
6e1eda6f
RM
884 else
885 errx(EXIT_FAILURE, _("unknown compatibility mode '%s'"), p);
852ce62b
KZ
886 }
887 /* use default if no optarg specified */
78498b7b 888 break;
0e6f4a20 889 case 'H':
1653f0b0
KZ
890 fdisk_save_user_geometry(cxt, 0,
891 strtou32_or_err(optarg,
892 _("invalid heads argument")),
893 0);
0e6f4a20
KZ
894 break;
895 case 'S':
1653f0b0
KZ
896 fdisk_save_user_geometry(cxt, 0, 0,
897 strtou32_or_err(optarg,
898 _("invalid sectors argument")));
0e6f4a20 899 break;
2b6fc908 900 case 'l':
5b72b3dd 901 act = ACT_LIST;
2b6fc908 902 break;
80a1712f 903 case 'L':
5d51dc2a 904 colormode = UL_COLORMODE_AUTO;
80a1712f
KZ
905 if (optarg)
906 colormode = colormode_or_err(optarg,
907 _("unsupported color mode"));
908 break;
fff8ad58
KZ
909 case 'o':
910 outarg = optarg;
911 break;
2b6fc908 912 case 's':
5b72b3dd 913 act = ACT_SHOWSIZE;
2b6fc908 914 break;
565b7da6
KZ
915 case 't':
916 {
917 struct fdisk_label *lb = NULL;
918
6a632136 919 while (fdisk_next_label(cxt, &lb) == 0)
565b7da6
KZ
920 fdisk_label_set_disabled(lb, 1);
921
6a632136 922 lb = fdisk_get_label(cxt, optarg);
565b7da6
KZ
923 if (!lb)
924 errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg);
925 fdisk_label_set_disabled(lb, 0);
1ce1a112 926 break;
565b7da6 927 }
2b6fc908 928 case 'u':
ec10aa67
KZ
929 if (optarg && *optarg == '=')
930 optarg++;
6a632136 931 if (fdisk_set_unit(cxt, optarg) != 0)
6e1eda6f 932 errx(EXIT_FAILURE, _("unsupported unit"));
2b6fc908 933 break;
e3a4aaa7
KZ
934 case 'V': /* preferred for util-linux */
935 case 'v': /* for backward compatibility only */
a2482eb3
DB
936 printf(UTIL_LINUX_VERSION);
937 return EXIT_SUCCESS;
cb9a4b00
KZ
938 case 'w':
939 wipemode = wipemode_from_string(optarg);
940 if (wipemode < 0)
941 errx(EXIT_FAILURE, _("unsupported wipe mode"));
942 break;
ba465623
KZ
943 case 'W':
944 pwipemode = wipemode_from_string(optarg);
945 if (pwipemode < 0)
946 errx(EXIT_FAILURE, _("unsupported wipe mode"));
947 break;
e1144767 948 case 'h':
6e1eda6f 949 usage();
354f8cc8
KZ
950 case OPT_BYTES:
951 fdisk_set_size_unit(cxt, FDISK_SIZEUNIT_BYTES);
952 break;
2b6fc908 953 default:
677ec86c 954 errtryhelp(EXIT_FAILURE);
2b6fc908 955 }
6dbe3af9 956 }
2b6fc908 957
6bcd192c
KZ
958 if (argc-optind != 1 && fdisk_has_user_device_properties(cxt))
959 warnx(_("The device properties (sector size and geometry) should"
960 " be used with one specified device only."));
7eda085c 961
d0c9ddc3 962 colors_init(colormode, "fdisk");
ffcf0540 963
5b72b3dd
KZ
964 switch (act) {
965 case ACT_LIST:
6a632136 966 fdisk_enable_listonly(cxt, 1);
fff8ad58 967 init_fields(cxt, outarg, NULL);
ffcf0540 968
2b6fc908
KZ
969 if (argc > optind) {
970 int k;
04c163ed 971 int ct = 0;
8fdd483c
KZ
972
973 for (rc = 0, k = optind; k < argc; k++) {
04c163ed
TV
974 if (ct)
975 fputs("\n\n", stdout);
8fdd483c
KZ
976
977 rc += print_device_pt(cxt, argv[k], 1, 0);
04c163ed 978 ct++;
04c163ed 979 }
8fdd483c
KZ
980 if (rc)
981 return EXIT_FAILURE;
ea4824f1 982 } else
d2c47697 983 print_all_devices_pt(cxt, 0);
5b72b3dd 984 break;
2b6fc908 985
5b72b3dd 986 case ACT_SHOWSIZE:
9564e46c 987 /* deprecated */
6e1eda6f
RM
988 if (argc - optind <= 0) {
989 warnx(_("bad usage"));
990 errtryhelp(EXIT_FAILURE);
991 }
6da365de 992 for (i = optind; i < argc; i++) {
0073a4cf
KZ
993 uintmax_t blks = get_dev_blocks(argv[i]);
994
5b72b3dd 995 if (argc - optind == 1)
0073a4cf 996 printf("%ju\n", blks);
2b6fc908 997 else
0073a4cf 998 printf("%s: %ju\n", argv[i], blks);
6dbe3af9 999 }
5b72b3dd 1000 break;
6dbe3af9 1001
5b72b3dd 1002 case ACT_FDISK:
6e1eda6f
RM
1003 if (argc-optind != 1) {
1004 warnx(_("bad usage"));
1005 errtryhelp(EXIT_FAILURE);
1006 }
7eda085c 1007
5b72b3dd 1008 /* Here starts interactive mode, use fdisk_{warn,info,..} functions */
496c979a 1009 color_scheme_enable("welcome", UL_COLOR_GREEN);
09f0c3d9 1010 fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING);
80a1712f
KZ
1011 color_disable();
1012 fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n"
1013 "Be careful before using the write command.\n"));
2d8988bd 1014
6a632136 1015 rc = fdisk_assign_device(cxt, argv[optind], 0);
e146ae4e 1016 if (rc == -EACCES) {
6a632136 1017 rc = fdisk_assign_device(cxt, argv[optind], 1);
e146ae4e 1018 if (rc == 0)
62eea9ce 1019 fdisk_warnx(cxt, _("Device is open in read-only mode."));
e146ae4e
KZ
1020 }
1021 if (rc)
2d8988bd
KZ
1022 err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
1023
5b72b3dd 1024 fflush(stdout);
56c07b96 1025
5635d195
KZ
1026 if (fdisk_get_collision(cxt))
1027 follow_wipe_mode(cxt);
cb9a4b00 1028
aa36c2cf 1029 if (!fdisk_has_label(cxt)) {
2d8988bd 1030 fdisk_info(cxt, _("Device does not contain a recognized partition table."));
5b72b3dd 1031 fdisk_create_disklabel(cxt, NULL);
433d05ff 1032
aa36c2cf 1033 } else if (fdisk_is_label(cxt, GPT) && fdisk_gpt_is_hybrid(cxt))
433d05ff 1034 fdisk_warnx(cxt, _(
09af3db4 1035 "A hybrid GPT was detected. You have to sync "
433d05ff 1036 "the hybrid MBR manually (expert command 'M')."));
6dbe3af9 1037
fff8ad58
KZ
1038 init_fields(cxt, outarg, NULL); /* -o <columns> */
1039
5b72b3dd
KZ
1040 while (1)
1041 process_fdisk_menu(&cxt);
1042 }
9777759a 1043
c7119037 1044 fdisk_unref_context(cxt);
5b72b3dd 1045 return EXIT_SUCCESS;
6dbe3af9 1046}