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