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