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