]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/fdisk.c
Merge branch 'meson/pidfd_getfd' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / disk-utils / fdisk.c
1 /*
2 * SPDX-License-Identifier: GPL-1.0-or-later
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 1 of the License, or
7 * (at your option) any later version.
8 *
9 * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
10 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
11 *
12 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
13 */
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <ctype.h>
20 #include <errno.h>
21 #include <getopt.h>
22 #include <sys/stat.h>
23 #include <sys/time.h>
24 #include <time.h>
25 #include <limits.h>
26 #include <signal.h>
27 #include <poll.h>
28 #include <libsmartcols.h>
29 #ifdef HAVE_LIBREADLINE
30 # define _FUNCTION_DEF
31 # include <readline/readline.h>
32 #endif
33
34 #include "c.h"
35 #include "xalloc.h"
36 #include "all-io.h"
37 #include "nls.h"
38 #include "rpmatch.h"
39 #include "blkdev.h"
40 #include "mbsalign.h"
41 #include "pathnames.h"
42 #include "canonicalize.h"
43 #include "strutils.h"
44 #include "closestream.h"
45 #include "pager.h"
46
47 #include "fdisk.h"
48
49 #include "pt-sun.h" /* to toggle flags */
50
51 #ifdef HAVE_LINUX_COMPILER_H
52 # include <linux/compiler.h>
53 #endif
54 #ifdef HAVE_LINUX_BLKPG_H
55 # include <linux/blkpg.h>
56 #endif
57
58 #ifdef __linux__
59 # ifdef HAVE_LINUX_FS_H
60 # include <linux/fs.h>
61 # endif
62 #endif
63
64 int pwipemode = WIPEMODE_AUTO;
65 int device_is_used;
66 int is_interactive;
67 struct fdisk_table *original_layout;
68
69 static int wipemode = WIPEMODE_AUTO;
70
71 /*
72 * fdisk debug stuff (see fdisk.h and include/debug.h)
73 */
74 UL_DEBUG_DEFINE_MASK(fdisk);
75 UL_DEBUG_DEFINE_MASKNAMES(fdisk) = UL_DEBUG_EMPTY_MASKNAMES;
76
77 static void fdiskprog_init_debug(void)
78 {
79 __UL_INIT_DEBUG_FROM_ENV(fdisk, FDISKPROG_DEBUG_, 0, FDISK_DEBUG);
80 }
81
82 static void reply_sighandler(int sig __attribute__((unused)))
83 {
84 DBG(ASK, ul_debug("got signal"));
85 }
86
87 static int reply_running;
88
89 #ifdef HAVE_LIBREADLINE
90 static char *reply_line;
91
92 static void reply_linehandler(char *line)
93 {
94 reply_line = line;
95 reply_running = 0;
96 rl_callback_handler_remove(); /* avoid duplicate prompt */
97 }
98 #endif
99
100 int get_user_reply(const char *prompt, char *buf, size_t bufsz)
101 {
102 struct sigaction oldact, act = {
103 .sa_handler = reply_sighandler
104 };
105 struct pollfd fds[] = {
106 { .fd = fileno(stdin), .events = POLLIN }
107 };
108 size_t sz;
109 int ret = 0;
110
111 DBG(ASK, ul_debug("asking for user reply %s", is_interactive ? "[interactive]" : ""));
112
113 sigemptyset(&act.sa_mask);
114 sigaction(SIGINT, &act, &oldact);
115
116 #ifdef HAVE_LIBREADLINE
117 if (is_interactive)
118 rl_callback_handler_install(prompt, reply_linehandler);
119 #endif
120 errno = 0;
121 reply_running = 1;
122 do {
123 int rc;
124
125 *buf = '\0';
126 #ifdef HAVE_LIBREADLINE
127 if (!is_interactive)
128 #endif
129 {
130 fputs(prompt, stdout);
131 fflush(stdout);
132 }
133
134 rc = poll(fds, 1, -1);
135 if (rc == -1 && errno == EINTR) { /* interrupted by signal */
136 DBG(ASK, ul_debug("cancel by CTRL+C"));
137 ret = -ECANCELED;
138 goto done;
139 }
140 if (rc == -1 && errno != EAGAIN) { /* error */
141 ret = -errno;
142 goto done;
143 }
144 #ifdef HAVE_LIBREADLINE
145 if (is_interactive) {
146 /* read input and copy to buf[] */
147 rl_callback_read_char();
148 if (!reply_running && reply_line) {
149 sz = strlen(reply_line);
150 if (sz == 0)
151 buf[sz++] = '\n';
152 else
153 memcpy(buf, reply_line, min(sz, bufsz));
154 buf[min(sz, bufsz - 1)] = '\0';
155 free(reply_line);
156 reply_line = NULL;
157 }
158 } else
159 #endif
160 {
161 if (!fgets(buf, bufsz, stdin))
162 *buf = '\0';
163 break;
164 }
165 } while (reply_running);
166
167 if (!*buf) {
168 DBG(ASK, ul_debug("cancel by CTRL+D"));
169 ret = -ECANCELED;
170 clearerr(stdin);
171 goto done;
172 }
173
174 /*
175 * cleanup the reply
176 */
177 sz = ltrim_whitespace((unsigned char *) buf);
178 if (sz && *(buf + sz - 1) == '\n')
179 *(buf + sz - 1) = '\0';
180
181 done:
182 #ifdef HAVE_LIBREADLINE
183 if (is_interactive)
184 rl_callback_handler_remove();
185 #endif
186 sigaction(SIGINT, &oldact, NULL);
187 DBG(ASK, ul_debug("user's reply: >>>%s<<< [rc=%d]", buf, ret));
188 return ret;
189 }
190
191 static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask,
192 char *buf, size_t bufsz)
193
194 {
195 const char *q = fdisk_ask_get_query(ask);
196 int dft = fdisk_ask_menu_get_default(ask);
197
198 if (q) {
199 fputs(q, stdout); /* print header */
200 fputc('\n', stdout);
201 }
202
203 do {
204 char prompt[128];
205 int key, c, rc;
206 const char *name, *desc;
207 size_t i = 0;
208
209 /* print menu items */
210 while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) {
211 if (desc)
212 fprintf(stdout, " %c %s (%s)\n", key, name, desc);
213 else
214 fprintf(stdout, " %c %s\n", key, name);
215 }
216
217 /* ask for key */
218 snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft);
219 rc = get_user_reply(prompt, buf, bufsz);
220 if (rc)
221 return rc;
222 if (!*buf) {
223 fdisk_info(cxt, _("Using default response %c."), dft);
224 c = dft;
225 } else
226 c = tolower(buf[0]);
227
228 /* check result */
229 i = 0;
230 while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) {
231 if (c == key) {
232 fdisk_ask_menu_set_result(ask, c);
233 return 0; /* success */
234 }
235 }
236 fdisk_warnx(cxt, _("Value out of range."));
237 } while (1);
238
239 return -EINVAL;
240 }
241
242
243 #define tochar(num) ((int) ('a' + num - 1))
244 static int ask_number(struct fdisk_context *cxt,
245 struct fdisk_ask *ask,
246 char *buf, size_t bufsz)
247 {
248 char prompt[128] = { '\0' };
249 const char *q = fdisk_ask_get_query(ask);
250 const char *range = fdisk_ask_number_get_range(ask);
251
252 uint64_t dflt = fdisk_ask_number_get_default(ask),
253 low = fdisk_ask_number_get_low(ask),
254 high = fdisk_ask_number_get_high(ask);
255 int inchar = fdisk_ask_number_inchars(ask);
256
257 assert(q);
258
259 DBG(ASK, ul_debug("asking for number "
260 "['%s', <%"PRIu64",%"PRIu64">, default=%"PRIu64", range: %s]",
261 q, low, high, dflt, range));
262
263 if (range && dflt >= low && dflt <= high) {
264 if (inchar)
265 snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "),
266 q, range, tochar(dflt));
267 else
268 snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
269 q, range, dflt);
270
271 } else if (dflt >= low && dflt <= high) {
272 if (inchar)
273 snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "),
274 q, tochar(low), tochar(high), tochar(dflt));
275 else
276 snprintf(prompt, sizeof(prompt),
277 _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
278 q, low, high, dflt);
279 } else if (inchar)
280 snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "),
281 q, tochar(low), tochar(high));
282 else
283 snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
284 q, low, high);
285
286 do {
287 int rc = get_user_reply(prompt, buf, bufsz);
288 uint64_t num = 0;
289
290 if (rc)
291 return rc;
292 if (!*buf && dflt >= low && dflt <= high)
293 return fdisk_ask_number_set_result(ask, dflt);
294
295 if (isdigit_string(buf)) {
296 char *end;
297
298 errno = 0;
299 num = strtoumax(buf, &end, 10);
300 if (errno || buf == end || (end && *end))
301 continue;
302 } else if (inchar && isalpha(*buf)) {
303 num = tolower(*buf) - 'a' + 1;
304 } else
305 rc = -EINVAL;
306
307 if (rc == 0 && num >= low && num <= high)
308 return fdisk_ask_number_set_result(ask, num);
309
310 fdisk_warnx(cxt, _("Value out of range."));
311 } while (1);
312
313 return -1;
314 }
315
316 static int ask_offset(struct fdisk_context *cxt,
317 struct fdisk_ask *ask,
318 char *buf, size_t bufsz)
319 {
320 char prompt[128] = { '\0' };
321 const char *q = fdisk_ask_get_query(ask);
322 const char *range = fdisk_ask_number_get_range(ask);
323
324 uint64_t dflt = fdisk_ask_number_get_default(ask),
325 low = fdisk_ask_number_get_low(ask),
326 high = fdisk_ask_number_get_high(ask),
327 base = fdisk_ask_number_get_base(ask);
328
329 assert(q);
330
331 DBG(ASK, ul_debug("asking for offset ['%s', <%"PRIu64",%"PRIu64">, base=%"PRIu64", default=%"PRIu64", range: %s]",
332 q, low, high, base, dflt, range));
333
334 if (range && dflt >= low && dflt <= high)
335 snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
336 q, range, dflt);
337 else if (dflt >= low && dflt <= high)
338 snprintf(prompt, sizeof(prompt),
339 _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
340 q, low, high, dflt);
341 else
342 snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
343 q, low, high);
344
345 do {
346 uintmax_t num = 0;
347 char sig = 0, *p;
348 int pwr = 0;
349
350 int rc = get_user_reply(prompt, buf, bufsz);
351 if (rc)
352 return rc;
353 if (!*buf && dflt >= low && dflt <= high)
354 return fdisk_ask_number_set_result(ask, dflt);
355
356 p = buf;
357 if (*p == '+' || *p == '-') {
358 sig = *buf;
359 p++;
360 }
361
362 rc = parse_size(p, &num, &pwr);
363 if (rc)
364 continue;
365 DBG(ASK, ul_debug("parsed size: %ju", num));
366 if (sig && pwr) {
367 /* +{size}{K,M,...} specified, the "num" is in bytes */
368 uint64_t unit = fdisk_ask_number_get_unit(ask);
369 num += unit/2; /* round */
370 num /= unit;
371 }
372 if (sig == '+')
373 num += base;
374 else if (sig == '-' && fdisk_ask_number_is_wrap_negative(ask))
375 num = high - num;
376 else if (sig == '-')
377 num = base - num;
378
379 DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]",
380 num, sig, pwr,
381 sig ? "relative" : "absolute"));
382 if (num >= low && num <= high) {
383 if (sig && pwr)
384 fdisk_ask_number_set_relative(ask, 1);
385 return fdisk_ask_number_set_result(ask, (uint64_t)num);
386 }
387 fdisk_warnx(cxt, _("Value out of range."));
388 } while (1);
389
390 return -1;
391 }
392
393 static unsigned int info_count;
394
395 static void fputs_info(struct fdisk_ask *ask, FILE *out)
396 {
397 const char *msg;
398 assert(ask);
399
400 msg = fdisk_ask_print_get_mesg(ask);
401 if (!msg)
402 return;
403 if (info_count == 1)
404 fputc('\n', out);
405
406 fputs(msg, out);
407 fputc('\n', out);
408 }
409
410 int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
411 void *data __attribute__((__unused__)))
412 {
413 int rc = 0;
414 char buf[BUFSIZ] = { '\0' };
415
416 assert(cxt);
417 assert(ask);
418
419 if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO)
420 info_count = 0;
421
422 switch(fdisk_ask_get_type(ask)) {
423 case FDISK_ASKTYPE_MENU:
424 return ask_menu(cxt, ask, buf, sizeof(buf));
425 case FDISK_ASKTYPE_NUMBER:
426 return ask_number(cxt, ask, buf, sizeof(buf));
427 case FDISK_ASKTYPE_OFFSET:
428 return ask_offset(cxt, ask, buf, sizeof(buf));
429 case FDISK_ASKTYPE_INFO:
430 if (!fdisk_is_listonly(cxt))
431 info_count++;
432 fputs_info(ask, stdout);
433 break;
434 case FDISK_ASKTYPE_WARNX:
435 fflush(stdout);
436 color_scheme_fenable("warn", UL_COLOR_RED, stderr);
437 fputs(fdisk_ask_print_get_mesg(ask), stderr);
438 color_fdisable(stderr);
439 fputc('\n', stderr);
440 break;
441 case FDISK_ASKTYPE_WARN:
442 fflush(stdout);
443 color_scheme_fenable("warn", UL_COLOR_RED, stderr);
444 fputs(fdisk_ask_print_get_mesg(ask), stderr);
445 errno = fdisk_ask_print_get_errno(ask);
446 fprintf(stderr, ": %m\n");
447 color_fdisable(stderr);
448 break;
449 case FDISK_ASKTYPE_YESNO:
450 fputc('\n', stdout);
451 do {
452 int x;
453 fputs(fdisk_ask_get_query(ask), stdout);
454 rc = get_user_reply(_(" [Y]es/[N]o: "), buf, sizeof(buf));
455 if (rc)
456 break;
457 x = rpmatch(buf);
458 if (x == RPMATCH_YES || x == RPMATCH_NO) {
459 fdisk_ask_yesno_set_result(ask, x);
460 break;
461 }
462 } while(1);
463 DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc));
464 break;
465 case FDISK_ASKTYPE_STRING:
466 {
467 char prmt[BUFSIZ];
468 snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask));
469 fputc('\n', stdout);
470 rc = get_user_reply(prmt, buf, sizeof(buf));
471 if (rc == 0)
472 fdisk_ask_string_set_result(ask, xstrdup(buf));
473 DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc));
474 break;
475 }
476 default:
477 warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
478 return -EINVAL;
479 }
480 return rc;
481 }
482
483 static struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt, int *canceled)
484 {
485 const char *q;
486 struct fdisk_label *lb;
487
488 assert(cxt);
489 lb = fdisk_get_label(cxt, NULL);
490
491 if (!lb)
492 return NULL;
493
494 *canceled = 0;
495
496 if (fdisk_label_has_parttypes_shortcuts(lb))
497 q = fdisk_label_has_code_parttypes(lb) ?
498 _("Hex code or alias (type L to list all): ") :
499 _("Partition type or alias (type L to list all): ");
500 else
501 q = fdisk_label_has_code_parttypes(lb) ?
502 _("Hex code (type L to list all codes): ") :
503 _("Partition type (type L to list all types): ");
504 do {
505 char buf[256] = { '\0' };
506 int rc = get_user_reply(q, buf, sizeof(buf));
507
508 if (rc) {
509 if (rc == -ECANCELED)
510 *canceled = 1;
511 break;
512 }
513
514 if (buf[1] == '\0' && toupper(*buf) == 'L')
515 list_partition_types(cxt);
516 else if (*buf) {
517 struct fdisk_parttype *t = fdisk_label_advparse_parttype(lb, buf,
518 FDISK_PARTTYPE_PARSE_DATA
519 | FDISK_PARTTYPE_PARSE_ALIAS
520 | FDISK_PARTTYPE_PARSE_NAME
521 | FDISK_PARTTYPE_PARSE_SEQNUM);
522 if (!t)
523 fdisk_info(cxt, _("Failed to parse '%s' partition type."), buf);
524 return t;
525 }
526 } while (1);
527
528 return NULL;
529 }
530
531
532 void list_partition_types(struct fdisk_context *cxt)
533 {
534 size_t ntypes = 0, next = 0;
535 struct fdisk_label *lb;
536 int pager = 0;
537
538 assert(cxt);
539 lb = fdisk_get_label(cxt, NULL);
540 if (!lb)
541 return;
542 ntypes = fdisk_label_get_nparttypes(lb);
543 if (!ntypes)
544 return;
545
546 if (fdisk_label_has_code_parttypes(lb)) {
547 /*
548 * Prints in 4 columns in format <hex> <name>
549 */
550 size_t last[4], done = 0, size;
551 int i;
552
553 size = ntypes;
554
555 for (i = 3; i >= 0; i--)
556 last[3 - i] = done += (size + i - done) / (i + 1);
557 i = done = 0;
558
559 do {
560 #define NAME_WIDTH 15
561 char name[NAME_WIDTH * MB_LEN_MAX];
562 size_t width = NAME_WIDTH;
563 const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, next);
564 size_t ret;
565
566 if (fdisk_parttype_get_name(t)) {
567 printf("%s%02x ", i ? " " : "\n",
568 fdisk_parttype_get_code(t));
569 ret = mbsalign(_(fdisk_parttype_get_name(t)),
570 name, sizeof(name),
571 &width, MBS_ALIGN_LEFT, 0);
572
573 if (ret == (size_t)-1 || ret >= sizeof(name))
574 printf("%-15.15s",
575 _(fdisk_parttype_get_name(t)));
576 else
577 fputs(name, stdout);
578 }
579
580 next = last[i++] + done;
581 if (i > 3 || next >= last[i]) {
582 i = 0;
583 next = ++done;
584 }
585 } while (done < last[0]);
586
587 putchar('\n');
588 } else {
589 /*
590 * Prints 1 column in format <idx> <name> <typestr>
591 */
592 size_t i;
593
594 pager_open();
595 pager = 1;
596
597 for (i = 0; i < ntypes; i++) {
598 const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, i);
599 printf("%3zu %-30s %s\n", i + 1,
600 fdisk_parttype_get_name(t),
601 fdisk_parttype_get_string(t));
602 }
603
604 }
605
606
607 /*
608 * Aliases
609 */
610 if (fdisk_label_has_parttypes_shortcuts(lb)) {
611 const char *alias = NULL, *typestr = NULL;
612 int rc = 0;
613
614 fputs(_("\nAliases:\n"), stdout);
615
616 for (next = 0; rc == 0 || rc == 2; next++) {
617 /* rc: <0 error, 0 success, 1 end, 2 deprecated */
618 rc = fdisk_label_get_parttype_shortcut(lb,
619 next, &typestr, NULL, &alias);
620 if (rc == 0)
621 printf(" %-14s - %s\n", alias, typestr);
622 }
623 }
624
625 if (pager)
626 pager_close();
627
628 }
629
630 void toggle_dos_compatibility_flag(struct fdisk_context *cxt)
631 {
632 struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
633 int flag;
634
635 if (!lb)
636 return;
637
638 flag = !fdisk_dos_is_compatible(lb);
639 fdisk_info(cxt, flag ?
640 _("DOS Compatibility flag is set (DEPRECATED!)") :
641 _("DOS Compatibility flag is not set"));
642
643 fdisk_dos_enable_compatible(lb, flag);
644
645 if (fdisk_is_label(cxt, DOS))
646 fdisk_reset_alignment(cxt); /* reset the current label */
647 }
648
649 static int strtosize_sectors(const char *str, unsigned long sector_size,
650 uintmax_t *res)
651 {
652 size_t len = strlen(str);
653 int insec = 0;
654 int rc;
655
656 if (!len)
657 return 0;
658
659 if (str[len - 1] == 'S' || str[len - 1] == 's') {
660 insec = 1;
661 str = strndup(str, len - 1); /* strip trailing 's' */
662 if (!str)
663 return -errno;
664 }
665
666 rc = strtosize(str, res);
667 if (rc)
668 return rc;
669
670 if (insec) {
671 *res *= sector_size;
672 free((void *)str);
673 }
674
675 return 0;
676 }
677
678 void resize_partition(struct fdisk_context *cxt)
679 {
680 struct fdisk_partition *pa = NULL, *npa = NULL, *next = NULL;
681 char *query = NULL, *response = NULL, *default_size;
682 struct fdisk_table *tb = NULL;
683 uint64_t max_size, size, secs;
684 size_t i;
685 int rc;
686
687 assert(cxt);
688
689 rc = fdisk_ask_partnum(cxt, &i, FALSE);
690 if (rc)
691 goto err;
692
693 rc = fdisk_partition_get_max_size(cxt, i, &max_size);
694 if (rc)
695 goto err;
696
697 max_size *= fdisk_get_sector_size(cxt);
698
699 default_size = size_to_human_string(0, max_size);
700 xasprintf(&query, _("New <size>{K,M,G,T,P} in bytes or <size>S in sectors (default %s)"),
701 default_size);
702 free(default_size);
703
704 rc = fdisk_ask_string(cxt, query, &response);
705 if (rc)
706 goto err;
707
708 size = max_size;
709 rc = strtosize_sectors(response, fdisk_get_sector_size(cxt), &size);
710 if (rc || size > max_size) {
711 fdisk_warnx(cxt, _("Invalid size"));
712 goto err;
713 }
714
715 npa = fdisk_new_partition();
716 if (!npa)
717 goto err;
718
719 secs = size / fdisk_get_sector_size(cxt);
720 fdisk_partition_size_explicit(npa, 1);
721 fdisk_partition_set_size(npa, secs);
722
723 rc = fdisk_set_partition(cxt, i, npa);
724 if (rc)
725 goto err;
726
727 fdisk_info(cxt, _("Partition %zu has been resized."), i + 1);
728
729 out:
730 free(query);
731 free(response);
732 fdisk_unref_partition(next);
733 fdisk_unref_partition(pa);
734 fdisk_unref_partition(npa);
735 fdisk_unref_table(tb);
736 return;
737
738 err:
739 fdisk_warnx(cxt, _("Could not resize partition %zu: %s"),
740 i + 1, strerror(-rc));
741 goto out;
742 }
743
744 void change_partition_type(struct fdisk_context *cxt)
745 {
746 size_t i;
747 struct fdisk_parttype *t = NULL;
748 struct fdisk_partition *pa = NULL;
749 const char *old = NULL;
750 int canceled = 0;
751
752 assert(cxt);
753
754 if (fdisk_ask_partnum(cxt, &i, FALSE))
755 return;
756
757 if (fdisk_get_partition(cxt, i, &pa)) {
758 fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
759 return;
760 }
761
762 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
763 old = t ? fdisk_parttype_get_name(t) : _("Unknown");
764
765 do {
766 t = ask_partition_type(cxt, &canceled);
767 if (canceled)
768 break;
769 } while (!t);
770
771 if (canceled == 0 && t && fdisk_set_partition_type(cxt, i, t) == 0)
772 fdisk_info(cxt,
773 _("Changed type of partition '%s' to '%s'."),
774 old, t ? fdisk_parttype_get_name(t) : _("Unknown"));
775 else
776 fdisk_info(cxt,
777 _("Type of partition %zu is unchanged: %s."),
778 i + 1, old);
779
780 fdisk_unref_partition(pa);
781 fdisk_unref_parttype(t);
782 }
783
784 #ifdef BLKDISCARD
785
786 static int do_discard(struct fdisk_context *cxt, struct fdisk_partition *pa)
787 {
788 char buf[512];
789 unsigned long ss;
790 uint64_t range[2];
791 int yes = 0;
792
793 ss = fdisk_get_sector_size(cxt);
794
795 range[0] = (uint64_t) fdisk_partition_get_start(pa);
796 range[1] = (uint64_t) fdisk_partition_get_size(pa);
797
798 snprintf(buf, sizeof(buf), _("All data in the region (%"PRIu64
799 "-%"PRIu64") will be lost! Continue?"),
800 range[0], range[0] + range[1] - 1);
801
802 range[0] *= (uint64_t) ss;
803 range[1] *= (uint64_t) ss;
804
805 fdisk_ask_yesno(cxt, buf, &yes);
806 if (!yes)
807 return 1;
808
809 errno = 0;
810 if (ioctl(fdisk_get_devfd(cxt), BLKDISCARD, &range)) {
811 fdisk_warn(cxt, _("BLKDISCARD ioctl failed"));
812 return -errno;
813 }
814 return 0;
815 }
816
817 static void discard_partition(struct fdisk_context *cxt)
818 {
819 struct fdisk_partition *pa = NULL;
820 size_t n = 0;
821
822 fdisk_info(cxt, _("\nThe partition sectors will be immediately discarded.\n"
823 "You can exit this dialog by pressing CTRL+C.\n"));
824
825 if (fdisk_ask_partnum(cxt, &n, FALSE))
826 goto done;
827 if (fdisk_get_partition(cxt, n, &pa)) {
828 fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), n + 1);
829 goto done;
830 }
831
832 if (!fdisk_partition_has_size(pa) || !fdisk_partition_has_start(pa)) {
833 fdisk_warnx(cxt, _("Partition %zu has an unspecified range."), n + 1);
834 goto done;
835 }
836
837 if (do_discard(cxt, pa) == 0)
838 fdisk_info(cxt, _("Discarded sectors on partition %zu."), n + 1);
839 done:
840 fdisk_unref_partition(pa);
841 }
842
843 static void discard_freespace(struct fdisk_context *cxt)
844 {
845 struct fdisk_partition *pa = NULL;
846 struct fdisk_table *tb = NULL;
847 size_t best = 0;
848 uintmax_t n = 0;
849 int ct;
850
851 ct = list_freespace_get_table(cxt, &tb, &best);
852 if (ct <= 0) {
853 fdisk_info(cxt, _("No free space."));
854 goto done;
855 }
856 fdisk_info(cxt, _("\nThe unused sectors will be immediately discarded.\n"
857 "You can exit this dialog by pressing CTRL+C.\n"));
858
859 if (fdisk_ask_number(cxt, 1, best + 1, (uintmax_t) ct,
860 _("Free space number"), &n) != 0)
861 goto done;
862
863 pa = fdisk_table_get_partition(tb, n - 1);
864 if (!pa)
865 goto done;
866
867 if (!fdisk_partition_has_size(pa) || !fdisk_partition_has_start(pa)) {
868 fdisk_warnx(cxt, _("Free space %"PRIu64 "has an unspecified range"), n);
869 goto done;
870 }
871
872 if (do_discard(cxt, pa) == 0)
873 fdisk_info(cxt, _("Discarded sectors on free space."));
874 done:
875 fdisk_unref_table(tb);
876 }
877
878 void discard_sectors(struct fdisk_context *cxt)
879 {
880 int c;
881
882 if (fdisk_is_readonly(cxt)) {
883 fdisk_warnx(cxt, _("Discarding sectors is not possible in read-only mode."));
884 return;
885 }
886
887 if (fdisk_ask_menu(cxt, _("Type of area to be discarded"),
888 &c, 'p', _("partition sectors"), 'p',
889 _("free space sectros"), 'f', NULL) != 0)
890 return;
891
892 switch (c) {
893 case 'p':
894 discard_partition(cxt);
895 break;
896 case 'f':
897 discard_freespace(cxt);
898 break;
899 }
900 }
901
902 #else /* !BLKDISCARD */
903 void discard_sectors(struct fdisk_context *cxt)
904 {
905 fdisk_warnx(cxt, _("Discard unsupported on your system."));
906 }
907 #endif /* BLKDISCARD */
908
909 int print_partition_info(struct fdisk_context *cxt)
910 {
911 struct fdisk_partition *pa = NULL;
912 int rc = 0;
913 size_t i, nfields;
914 int *fields = NULL;
915 struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
916
917 if ((rc = fdisk_ask_partnum(cxt, &i, FALSE)))
918 return rc;
919
920 if ((rc = fdisk_get_partition(cxt, i, &pa))) {
921 fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
922 return rc;
923 }
924
925 if ((rc = fdisk_label_get_fields_ids_all(lb, cxt, &fields, &nfields)))
926 goto clean_data;
927
928 for (i = 0; i < nfields; ++i) {
929 int id = fields[i];
930 char *data = NULL;
931 const struct fdisk_field *fd = fdisk_label_get_field(lb, id);
932
933 if (!fd)
934 continue;
935
936 rc = fdisk_partition_to_string(pa, cxt, id, &data);
937 if (rc < 0)
938 goto clean_data;
939 if (!data || !*data)
940 continue;
941 fdisk_info(cxt, "%15s: %s", fdisk_field_get_name(fd), data);
942 free(data);
943 }
944
945 clean_data:
946 fdisk_unref_partition(pa);
947 free(fields);
948 return rc;
949 }
950
951 static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz)
952 {
953 size_t next;
954 const unsigned char *p0 = buf + i;
955
956 for (next = i + 16; next < sz; next += 16) {
957 if (memcmp(p0, buf + next, 16) != 0)
958 break;
959 }
960
961 return next == i + 16 ? i : next;
962 }
963
964 static void dump_buffer(off_t base, unsigned char *buf, size_t sz)
965 {
966 size_t i, l, next = 0;
967
968 if (!buf)
969 return;
970 for (i = 0, l = 0; i < sz; i++, l++) {
971 if (l == 0) {
972 if (!next)
973 next = skip_empty(buf, i, sz);
974 printf("%08jx ", (intmax_t)base + i);
975 }
976 printf(" %02x", buf[i]);
977 if (l == 7) /* words separator */
978 fputs(" ", stdout);
979 else if (l == 15) {
980 fputc('\n', stdout); /* next line */
981 l = -1;
982 if (next > i) {
983 printf("*\n");
984 i = next - 1;
985 }
986 next = 0;
987 }
988 }
989 if (l > 0)
990 printf("\n");
991 }
992
993 static void dump_blkdev(struct fdisk_context *cxt, const char *name,
994 uint64_t offset, size_t size)
995 {
996 int fd = fdisk_get_devfd(cxt);
997
998 fdisk_info(cxt, _("\n%s: offset = %"PRIu64", size = %zu bytes."),
999 name, offset, size);
1000
1001 assert(fd >= 0);
1002
1003 if (lseek(fd, (off_t) offset, SEEK_SET) == (off_t) -1)
1004 fdisk_warn(cxt, _("cannot seek"));
1005 else {
1006 unsigned char *buf = xmalloc(size);
1007
1008 if (read_all(fd, (char *) buf, size) != (ssize_t) size)
1009 fdisk_warn(cxt, _("cannot read"));
1010 else
1011 dump_buffer(offset, buf, size);
1012 free(buf);
1013 }
1014 }
1015
1016 void dump_firstsector(struct fdisk_context *cxt)
1017 {
1018 assert(cxt);
1019
1020 dump_blkdev(cxt, _("First sector"), 0, fdisk_get_sector_size(cxt));
1021 }
1022
1023 void dump_disklabel(struct fdisk_context *cxt)
1024 {
1025 int i = 0;
1026 const char *name = NULL;
1027 uint64_t offset = 0;
1028 size_t size = 0;
1029
1030 assert(cxt);
1031
1032 while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size)
1033 dump_blkdev(cxt, name, offset, size);
1034 }
1035
1036 static fdisk_sector_t get_dev_blocks(char *dev)
1037 {
1038 int fd, ret;
1039 fdisk_sector_t size;
1040
1041 if ((fd = open(dev, O_RDONLY|O_NONBLOCK)) < 0)
1042 err(EXIT_FAILURE, _("cannot open %s"), dev);
1043 ret = blkdev_get_sectors(fd, (unsigned long long *) &size);
1044 close(fd);
1045 if (ret < 0)
1046 err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
1047 return size/2;
1048 }
1049
1050
1051 void follow_wipe_mode(struct fdisk_context *cxt)
1052 {
1053 int dowipe = wipemode == WIPEMODE_ALWAYS ? 1 : 0;
1054
1055 if (isatty(STDIN_FILENO) && wipemode == WIPEMODE_AUTO)
1056 dowipe = 1; /* do it in interactive mode */
1057
1058 if (fdisk_is_ptcollision(cxt) && wipemode != WIPEMODE_NEVER)
1059 dowipe = 1; /* always remove old PT */
1060
1061 fdisk_enable_wipe(cxt, dowipe);
1062 if (dowipe)
1063 fdisk_warnx(cxt, _(
1064 "The device contains '%s' signature and it will be removed by a write command. "
1065 "See fdisk(8) man page and --wipe option for more details."),
1066 fdisk_get_collision(cxt));
1067 else
1068 fdisk_warnx(cxt, _(
1069 "The device contains '%s' signature and it may remain on the device. "
1070 "It is recommended to wipe the device with wipefs(8) or "
1071 "fdisk --wipe, in order to avoid possible collisions."),
1072 fdisk_get_collision(cxt));
1073 }
1074
1075 static void __attribute__((__noreturn__)) usage(void)
1076 {
1077 FILE *out = stdout;
1078
1079 fputs(USAGE_HEADER, out);
1080
1081 fprintf(out,
1082 _(" %1$s [options] <disk> change partition table\n"
1083 " %1$s [options] -l [<disk>...] list partition table(s)\n"),
1084 program_invocation_short_name);
1085
1086 fputs(USAGE_SEPARATOR, out);
1087 fputs(_("Display or manipulate a disk partition table.\n"), out);
1088
1089 fputs(USAGE_OPTIONS, out);
1090 fputs(_(" -b, --sector-size <size> physical and logical sector size\n"), out);
1091 fputs(_(" -B, --protect-boot don't erase bootbits when creating a new label\n"), out);
1092 fputs(_(" -c, --compatibility[=<mode>] mode is 'dos' or 'nondos' (default)\n"), out);
1093 fprintf(out,
1094 _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
1095 fprintf(out,
1096 " %s\n", USAGE_COLORS_DEFAULT);
1097 fputs(_(" -l, --list display partitions and exit\n"), out);
1098 fputs(_(" -x, --list-details like --list but with more details\n"), out);
1099
1100 fputs(_(" -n, --noauto-pt don't create default partition table on empty devices\n"), out);
1101 fputs(_(" -o, --output <list> output columns\n"), out);
1102 fputs(_(" -t, --type <type> recognize specified partition table type only\n"), out);
1103 fputs(_(" -u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out);
1104 fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out);
1105 fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out);
1106 fprintf(out,
1107 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
1108 fprintf(out,
1109 _(" -w, --wipe <mode> wipe signatures (%s, %s or %s)\n"), "auto", "always", "never");
1110 fprintf(out,
1111 _(" -W, --wipe-partitions <mode> wipe signatures from new partitions (%s, %s or %s)\n"), "auto", "always", "never");
1112
1113 fputs(USAGE_SEPARATOR, out);
1114 fputs(_(" -C, --cylinders <number> specify the number of cylinders\n"), out);
1115 fputs(_(" -H, --heads <number> specify the number of heads\n"), out);
1116 fputs(_(" -S, --sectors <number> specify the number of sectors per track\n"), out);
1117
1118 fputs(USAGE_SEPARATOR, out);
1119 fprintf(out, USAGE_HELP_OPTIONS(31));
1120
1121 list_available_columns(out);
1122
1123 fprintf(out, USAGE_MAN_TAIL("fdisk(8)"));
1124 exit(EXIT_SUCCESS);
1125 }
1126
1127
1128 enum {
1129 ACT_FDISK = 0, /* default */
1130 ACT_LIST,
1131 ACT_LIST_DETAILS,
1132 ACT_SHOWSIZE
1133 };
1134
1135 int main(int argc, char **argv)
1136 {
1137 int rc, i, c, act = ACT_FDISK, noauto_pt = 0;
1138 int colormode = UL_COLORMODE_UNDEF;
1139 struct fdisk_context *cxt;
1140 char *outarg = NULL;
1141 const char *devname, *lockmode = NULL;
1142 enum {
1143 OPT_BYTES = CHAR_MAX + 1,
1144 OPT_LOCK
1145 };
1146 static const struct option longopts[] = {
1147 { "bytes", no_argument, NULL, OPT_BYTES },
1148 { "color", optional_argument, NULL, 'L' },
1149 { "compatibility", optional_argument, NULL, 'c' },
1150 { "cylinders", required_argument, NULL, 'C' },
1151 { "heads", required_argument, NULL, 'H' },
1152 { "sectors", required_argument, NULL, 'S' },
1153 { "getsz", no_argument, NULL, 's' },
1154 { "help", no_argument, NULL, 'h' },
1155 { "list", no_argument, NULL, 'l' },
1156 { "list-details", no_argument, NULL, 'x' },
1157 { "lock", optional_argument, NULL, OPT_LOCK },
1158 { "noauto-pt", no_argument, NULL, 'n' },
1159 { "sector-size", required_argument, NULL, 'b' },
1160 { "type", required_argument, NULL, 't' },
1161 { "units", optional_argument, NULL, 'u' },
1162 { "version", no_argument, NULL, 'V' },
1163 { "output", required_argument, NULL, 'o' },
1164 { "protect-boot", no_argument, NULL, 'B' },
1165 { "wipe", required_argument, NULL, 'w' },
1166 { "wipe-partitions",required_argument, NULL, 'W' },
1167 { NULL, 0, NULL, 0 }
1168 };
1169
1170 setlocale(LC_ALL, "");
1171 bindtextdomain(PACKAGE, LOCALEDIR);
1172 textdomain(PACKAGE);
1173 close_stdout_atexit();
1174
1175 fdisk_init_debug(0);
1176 scols_init_debug(0);
1177 fdiskprog_init_debug();
1178
1179 cxt = fdisk_new_context();
1180 if (!cxt)
1181 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
1182
1183 fdisk_set_ask(cxt, ask_callback, NULL);
1184
1185 while ((c = getopt_long(argc, argv, "b:Bc::C:hH:lL::no:sS:t:u::vVw:W:x",
1186 longopts, NULL)) != -1) {
1187 switch (c) {
1188 case 'b':
1189 {
1190 size_t sz = strtou32_or_err(optarg,
1191 _("invalid sector size argument"));
1192 if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096)
1193 errx(EXIT_FAILURE, _("invalid sector size argument"));
1194 fdisk_save_user_sector_size(cxt, sz, sz);
1195 break;
1196 }
1197 case 'B':
1198 fdisk_enable_bootbits_protection(cxt, 1);
1199 break;
1200 case 'C':
1201 fdisk_save_user_geometry(cxt,
1202 strtou32_or_err(optarg,
1203 _("invalid cylinders argument")),
1204 0, 0);
1205 break;
1206 case 'c':
1207 if (optarg) {
1208 /* this setting is independent on the current
1209 * actively used label
1210 */
1211 char *p = *optarg == '=' ? optarg + 1 : optarg;
1212 struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
1213
1214 if (!lb)
1215 err(EXIT_FAILURE, _("not found DOS label driver"));
1216 if (strcmp(p, "dos") == 0)
1217 fdisk_dos_enable_compatible(lb, TRUE);
1218 else if (strcmp(p, "nondos") == 0)
1219 fdisk_dos_enable_compatible(lb, FALSE);
1220 else
1221 errx(EXIT_FAILURE, _("unknown compatibility mode '%s'"), p);
1222 }
1223 /* use default if no optarg specified */
1224 break;
1225 case 'H':
1226 fdisk_save_user_geometry(cxt, 0,
1227 strtou32_or_err(optarg,
1228 _("invalid heads argument")),
1229 0);
1230 break;
1231 case 'S':
1232 fdisk_save_user_geometry(cxt, 0, 0,
1233 strtou32_or_err(optarg,
1234 _("invalid sectors argument")));
1235 break;
1236 case 'l':
1237 act = ACT_LIST;
1238 break;
1239 case 'x':
1240 act = ACT_LIST_DETAILS;
1241 break;
1242 case 'L':
1243 colormode = UL_COLORMODE_AUTO;
1244 if (optarg)
1245 colormode = colormode_or_err(optarg,
1246 _("unsupported color mode"));
1247 break;
1248 case 'n':
1249 noauto_pt = 1;
1250 break;
1251 case 'o':
1252 outarg = optarg;
1253 break;
1254 case 's':
1255 act = ACT_SHOWSIZE;
1256 break;
1257 case 't':
1258 {
1259 struct fdisk_label *lb = NULL;
1260
1261 while (fdisk_next_label(cxt, &lb) == 0)
1262 fdisk_label_set_disabled(lb, 1);
1263
1264 lb = fdisk_get_label(cxt, optarg);
1265 if (!lb)
1266 errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg);
1267 fdisk_label_set_disabled(lb, 0);
1268 break;
1269 }
1270 case 'u':
1271 if (optarg && *optarg == '=')
1272 optarg++;
1273 if (fdisk_set_unit(cxt, optarg) != 0)
1274 errx(EXIT_FAILURE, _("unsupported unit"));
1275 break;
1276 case 'V': /* preferred for util-linux */
1277 case 'v': /* for backward compatibility only */
1278 print_version(EXIT_SUCCESS);
1279 case 'w':
1280 wipemode = wipemode_from_string(optarg);
1281 if (wipemode < 0)
1282 errx(EXIT_FAILURE, _("unsupported wipe mode"));
1283 break;
1284 case 'W':
1285 pwipemode = wipemode_from_string(optarg);
1286 if (pwipemode < 0)
1287 errx(EXIT_FAILURE, _("unsupported wipe mode"));
1288 break;
1289 case 'h':
1290 usage();
1291 case OPT_BYTES:
1292 fdisk_set_size_unit(cxt, FDISK_SIZEUNIT_BYTES);
1293 break;
1294 case OPT_LOCK:
1295 lockmode = "1";
1296 if (optarg) {
1297 if (*optarg == '=')
1298 optarg++;
1299 lockmode = optarg;
1300 }
1301 break;
1302 default:
1303 errtryhelp(EXIT_FAILURE);
1304 }
1305 }
1306
1307 if (argc-optind != 1 && fdisk_has_user_device_properties(cxt))
1308 warnx(_("The device properties (sector size and geometry) should"
1309 " be used with one specified device only."));
1310
1311 colors_init(colormode, "fdisk");
1312 is_interactive = isatty(STDIN_FILENO);
1313
1314 switch (act) {
1315 case ACT_LIST:
1316 case ACT_LIST_DETAILS:
1317 fdisk_enable_listonly(cxt, 1);
1318
1319 if (act == ACT_LIST_DETAILS)
1320 fdisk_enable_details(cxt, 1);
1321
1322 init_fields(cxt, outarg, NULL);
1323
1324 if (argc > optind) {
1325 int k;
1326
1327 for (rc = 0, k = optind; k < argc; k++)
1328 rc += print_device_pt(cxt, argv[k], 1, 0, k != optind);
1329
1330 if (rc)
1331 return EXIT_FAILURE;
1332 } else
1333 print_all_devices_pt(cxt, 0);
1334 break;
1335
1336 case ACT_SHOWSIZE:
1337 /* deprecated */
1338 if (argc - optind <= 0) {
1339 warnx(_("bad usage"));
1340 errtryhelp(EXIT_FAILURE);
1341 }
1342 for (i = optind; i < argc; i++) {
1343 uintmax_t blks = get_dev_blocks(argv[i]);
1344
1345 if (argc - optind == 1)
1346 printf("%ju\n", blks);
1347 else
1348 printf("%s: %ju\n", argv[i], blks);
1349 }
1350 break;
1351
1352 case ACT_FDISK:
1353 if (argc-optind != 1) {
1354 warnx(_("bad usage"));
1355 errtryhelp(EXIT_FAILURE);
1356 }
1357
1358 /* Here starts interactive mode, use fdisk_{warn,info,..} functions */
1359 color_scheme_enable("welcome", UL_COLOR_GREEN);
1360 fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING);
1361 color_disable();
1362 fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n"
1363 "Be careful before using the write command.\n"));
1364
1365 devname = argv[optind];
1366 rc = fdisk_assign_device(cxt, devname, 0);
1367 if (rc == -EACCES) {
1368 rc = fdisk_assign_device(cxt, devname, 1);
1369 if (rc == 0)
1370 fdisk_warnx(cxt, _("Device is open in read-only mode."));
1371 }
1372 if (rc)
1373 err(EXIT_FAILURE, _("cannot open %s"), devname);
1374
1375 if (fdisk_device_is_used(cxt))
1376 fdisk_warnx(cxt, _(
1377 "This disk is currently in use - repartitioning is probably a bad idea.\n"
1378 "It's recommended to umount all file systems, and swapoff all swap\n"
1379 "partitions on this disk.\n"));
1380
1381 fflush(stdout);
1382
1383 if (!fdisk_is_readonly(cxt)
1384 && blkdev_lock(fdisk_get_devfd(cxt), devname, lockmode) != 0) {
1385 fdisk_deassign_device(cxt, 1);
1386 fdisk_unref_context(cxt);
1387 return EXIT_FAILURE;
1388 }
1389
1390 if (fdisk_get_collision(cxt))
1391 follow_wipe_mode(cxt);
1392
1393 if (!fdisk_has_label(cxt)) {
1394 fdisk_info(cxt, _("Device does not contain a recognized partition table."));
1395 if (!noauto_pt)
1396 fdisk_create_disklabel(cxt, NULL);
1397
1398 } else if (fdisk_is_label(cxt, GPT) && fdisk_gpt_is_hybrid(cxt))
1399 fdisk_warnx(cxt, _(
1400 "A hybrid GPT was detected. You have to sync "
1401 "the hybrid MBR manually (expert command 'M')."));
1402
1403 init_fields(cxt, outarg, NULL); /* -o <columns> */
1404
1405 if (!fdisk_is_readonly(cxt)) {
1406 fdisk_get_partitions(cxt, &original_layout);
1407 device_is_used = fdisk_device_is_used(cxt);
1408 }
1409
1410 while (1)
1411 process_fdisk_menu(&cxt);
1412 }
1413
1414 fdisk_unref_context(cxt);
1415 return EXIT_SUCCESS;
1416 }