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