2 * SPDX-License-Identifier: GPL-1.0-or-later
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.
9 * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
10 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
12 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
28 #include <libsmartcols.h>
29 #ifdef HAVE_LIBREADLINE
30 # define _FUNCTION_DEF
31 # include <readline/readline.h>
41 #include "pathnames.h"
42 #include "canonicalize.h"
44 #include "closestream.h"
49 #include "pt-sun.h" /* to toggle flags */
51 #ifdef HAVE_LINUX_COMPILER_H
52 # include <linux/compiler.h>
54 #ifdef HAVE_LINUX_BLKPG_H
55 # include <linux/blkpg.h>
59 # ifdef HAVE_LINUX_FS_H
60 # include <linux/fs.h>
64 int pwipemode
= WIPEMODE_AUTO
;
67 struct fdisk_table
*original_layout
;
69 static int wipemode
= WIPEMODE_AUTO
;
72 * fdisk debug stuff (see fdisk.h and include/debug.h)
74 UL_DEBUG_DEFINE_MASK(fdisk
);
75 UL_DEBUG_DEFINE_MASKNAMES(fdisk
) = UL_DEBUG_EMPTY_MASKNAMES
;
77 static void fdiskprog_init_debug(void)
79 __UL_INIT_DEBUG_FROM_ENV(fdisk
, FDISKPROG_DEBUG_
, 0, FDISK_DEBUG
);
82 static void reply_sighandler(int sig
__attribute__((unused
)))
84 DBG(ASK
, ul_debug("got signal"));
87 static int reply_running
;
89 #ifdef HAVE_LIBREADLINE
90 static char *reply_line
;
92 static void reply_linehandler(char *line
)
96 rl_callback_handler_remove(); /* avoid duplicate prompt */
100 int get_user_reply(const char *prompt
, char *buf
, size_t bufsz
)
102 struct sigaction oldact
, act
= {
103 .sa_handler
= reply_sighandler
105 struct pollfd fds
[] = {
106 { .fd
= fileno(stdin
), .events
= POLLIN
}
111 DBG(ASK
, ul_debug("asking for user reply %s", is_interactive
? "[interactive]" : ""));
113 sigemptyset(&act
.sa_mask
);
114 sigaction(SIGINT
, &act
, &oldact
);
116 #ifdef HAVE_LIBREADLINE
118 rl_callback_handler_install(prompt
, reply_linehandler
);
126 #ifdef HAVE_LIBREADLINE
130 fputs(prompt
, stdout
);
134 rc
= poll(fds
, 1, -1);
135 if (rc
== -1 && errno
== EINTR
) { /* interrupted by signal */
136 DBG(ASK
, ul_debug("cancel by CTRL+C"));
140 if (rc
== -1 && errno
!= EAGAIN
) { /* error */
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
);
153 memcpy(buf
, reply_line
, min(sz
, bufsz
));
154 buf
[min(sz
, bufsz
- 1)] = '\0';
161 if (!fgets(buf
, bufsz
, stdin
))
165 } while (reply_running
);
168 DBG(ASK
, ul_debug("cancel by CTRL+D"));
177 sz
= ltrim_whitespace((unsigned char *) buf
);
178 if (sz
&& *(buf
+ sz
- 1) == '\n')
179 *(buf
+ sz
- 1) = '\0';
182 #ifdef HAVE_LIBREADLINE
184 rl_callback_handler_remove();
186 sigaction(SIGINT
, &oldact
, NULL
);
187 DBG(ASK
, ul_debug("user's reply: >>>%s<<< [rc=%d]", buf
, ret
));
191 static int ask_menu(struct fdisk_context
*cxt
, struct fdisk_ask
*ask
,
192 char *buf
, size_t bufsz
)
195 const char *q
= fdisk_ask_get_query(ask
);
196 int dft
= fdisk_ask_menu_get_default(ask
);
199 fputs(q
, stdout
); /* print header */
206 const char *name
, *desc
;
209 /* print menu items */
210 while (fdisk_ask_menu_get_item(ask
, i
++, &key
, &name
, &desc
) == 0) {
212 fprintf(stdout
, " %c %s (%s)\n", key
, name
, desc
);
214 fprintf(stdout
, " %c %s\n", key
, name
);
218 snprintf(prompt
, sizeof(prompt
), _("Select (default %c): "), dft
);
219 rc
= get_user_reply(prompt
, buf
, bufsz
);
223 fdisk_info(cxt
, _("Using default response %c."), dft
);
230 while (fdisk_ask_menu_get_item(ask
, i
++, &key
, NULL
, NULL
) == 0) {
232 fdisk_ask_menu_set_result(ask
, c
);
233 return 0; /* success */
236 fdisk_warnx(cxt
, _("Value out of range."));
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
)
248 char prompt
[128] = { '\0' };
249 const char *q
= fdisk_ask_get_query(ask
);
250 const char *range
= fdisk_ask_number_get_range(ask
);
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
);
259 DBG(ASK
, ul_debug("asking for number "
260 "['%s', <%"PRIu64
",%"PRIu64
">, default=%"PRIu64
", range: %s]",
261 q
, low
, high
, dflt
, range
));
263 if (range
&& dflt
>= low
&& dflt
<= high
) {
265 snprintf(prompt
, sizeof(prompt
), _("%s (%s, default %c): "),
266 q
, range
, tochar(dflt
));
268 snprintf(prompt
, sizeof(prompt
), _("%s (%s, default %"PRIu64
"): "),
271 } else if (dflt
>= low
&& dflt
<= high
) {
273 snprintf(prompt
, sizeof(prompt
), _("%s (%c-%c, default %c): "),
274 q
, tochar(low
), tochar(high
), tochar(dflt
));
276 snprintf(prompt
, sizeof(prompt
),
277 _("%s (%"PRIu64
"-%"PRIu64
", default %"PRIu64
"): "),
280 snprintf(prompt
, sizeof(prompt
), _("%s (%c-%c): "),
281 q
, tochar(low
), tochar(high
));
283 snprintf(prompt
, sizeof(prompt
), _("%s (%"PRIu64
"-%"PRIu64
"): "),
287 int rc
= get_user_reply(prompt
, buf
, bufsz
);
292 if (!*buf
&& dflt
>= low
&& dflt
<= high
)
293 return fdisk_ask_number_set_result(ask
, dflt
);
295 if (isdigit_string(buf
)) {
299 num
= strtoumax(buf
, &end
, 10);
300 if (errno
|| buf
== end
|| (end
&& *end
))
302 } else if (inchar
&& isalpha(*buf
)) {
303 num
= tolower(*buf
) - 'a' + 1;
307 if (rc
== 0 && num
>= low
&& num
<= high
)
308 return fdisk_ask_number_set_result(ask
, num
);
310 fdisk_warnx(cxt
, _("Value out of range."));
316 static int ask_offset(struct fdisk_context
*cxt
,
317 struct fdisk_ask
*ask
,
318 char *buf
, size_t bufsz
)
320 char prompt
[128] = { '\0' };
321 const char *q
= fdisk_ask_get_query(ask
);
322 const char *range
= fdisk_ask_number_get_range(ask
);
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
);
331 DBG(ASK
, ul_debug("asking for offset ['%s', <%"PRIu64
",%"PRIu64
">, base=%"PRIu64
", default=%"PRIu64
", range: %s]",
332 q
, low
, high
, base
, dflt
, range
));
334 if (range
&& dflt
>= low
&& dflt
<= high
)
335 snprintf(prompt
, sizeof(prompt
), _("%s (%s, default %"PRIu64
"): "),
337 else if (dflt
>= low
&& dflt
<= high
)
338 snprintf(prompt
, sizeof(prompt
),
339 _("%s (%"PRIu64
"-%"PRIu64
", default %"PRIu64
"): "),
342 snprintf(prompt
, sizeof(prompt
), _("%s (%"PRIu64
"-%"PRIu64
"): "),
350 int rc
= get_user_reply(prompt
, buf
, bufsz
);
353 if (!*buf
&& dflt
>= low
&& dflt
<= high
)
354 return fdisk_ask_number_set_result(ask
, dflt
);
357 if (*p
== '+' || *p
== '-') {
362 rc
= parse_size(p
, &num
, &pwr
);
365 DBG(ASK
, ul_debug("parsed size: %ju", num
));
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 */
374 else if (sig
== '-' && fdisk_ask_number_is_wrap_negative(ask
))
379 DBG(ASK
, ul_debug("final offset: %ju [sig: %c, power: %d, %s]",
381 sig
? "relative" : "absolute"));
382 if (num
>= low
&& num
<= high
) {
384 fdisk_ask_number_set_relative(ask
, 1);
385 return fdisk_ask_number_set_result(ask
, (uint64_t)num
);
387 fdisk_warnx(cxt
, _("Value out of range."));
393 static unsigned int info_count
;
395 static void fputs_info(struct fdisk_ask
*ask
, FILE *out
)
400 msg
= fdisk_ask_print_get_mesg(ask
);
410 int ask_callback(struct fdisk_context
*cxt
, struct fdisk_ask
*ask
,
411 void *data
__attribute__((__unused__
)))
414 char buf
[BUFSIZ
] = { '\0' };
419 if (fdisk_ask_get_type(ask
) != FDISK_ASKTYPE_INFO
)
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
))
432 fputs_info(ask
, stdout
);
434 case FDISK_ASKTYPE_WARNX
:
436 color_scheme_fenable("warn", UL_COLOR_RED
, stderr
);
437 fputs(fdisk_ask_print_get_mesg(ask
), stderr
);
438 color_fdisable(stderr
);
441 case FDISK_ASKTYPE_WARN
:
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
);
449 case FDISK_ASKTYPE_YESNO
:
453 fputs(fdisk_ask_get_query(ask
), stdout
);
454 rc
= get_user_reply(_(" [Y]es/[N]o: "), buf
, sizeof(buf
));
458 if (x
== RPMATCH_YES
|| x
== RPMATCH_NO
) {
459 fdisk_ask_yesno_set_result(ask
, x
);
463 DBG(ASK
, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf
, rc
));
465 case FDISK_ASKTYPE_STRING
:
468 snprintf(prmt
, sizeof(prmt
), "%s: ", fdisk_ask_get_query(ask
));
470 rc
= get_user_reply(prmt
, buf
, sizeof(buf
));
472 fdisk_ask_string_set_result(ask
, xstrdup(buf
));
473 DBG(ASK
, ul_debug("string ask: reply '%s' [rc=%d]", buf
, rc
));
477 warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask
));
483 static struct fdisk_parttype
*ask_partition_type(struct fdisk_context
*cxt
, int *canceled
)
486 struct fdisk_label
*lb
;
489 lb
= fdisk_get_label(cxt
, NULL
);
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): ");
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): ");
505 char buf
[256] = { '\0' };
506 int rc
= get_user_reply(q
, buf
, sizeof(buf
));
509 if (rc
== -ECANCELED
)
514 if (buf
[1] == '\0' && toupper(*buf
) == 'L')
515 list_partition_types(cxt
);
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
);
523 fdisk_info(cxt
, _("Failed to parse '%s' partition type."), buf
);
532 void list_partition_types(struct fdisk_context
*cxt
)
534 size_t ntypes
= 0, next
= 0;
535 struct fdisk_label
*lb
;
539 lb
= fdisk_get_label(cxt
, NULL
);
542 ntypes
= fdisk_label_get_nparttypes(lb
);
546 if (fdisk_label_has_code_parttypes(lb
)) {
548 * Prints in 4 columns in format <hex> <name>
550 size_t last
[4], done
= 0, size
;
555 for (i
= 3; i
>= 0; i
--)
556 last
[3 - i
] = done
+= (size
+ i
- done
) / (i
+ 1);
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
);
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
)),
571 &width
, MBS_ALIGN_LEFT
, 0);
573 if (ret
== (size_t)-1 || ret
>= sizeof(name
))
575 _(fdisk_parttype_get_name(t
)));
580 next
= last
[i
++] + done
;
581 if (i
> 3 || next
>= last
[i
]) {
585 } while (done
< last
[0]);
590 * Prints 1 column in format <idx> <name> <typestr>
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
));
610 if (fdisk_label_has_parttypes_shortcuts(lb
)) {
611 const char *alias
= NULL
, *typestr
= NULL
;
614 fputs(_("\nAliases:\n"), stdout
);
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
);
621 printf(" %-14s - %s\n", alias
, typestr
);
630 void toggle_dos_compatibility_flag(struct fdisk_context
*cxt
)
632 struct fdisk_label
*lb
= fdisk_get_label(cxt
, "dos");
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"));
643 fdisk_dos_enable_compatible(lb
, flag
);
645 if (fdisk_is_label(cxt
, DOS
))
646 fdisk_reset_alignment(cxt
); /* reset the current label */
649 static int strtosize_sectors(const char *str
, unsigned long sector_size
,
652 size_t len
= strlen(str
);
659 if (str
[len
- 1] == 'S' || str
[len
- 1] == 's') {
661 str
= strndup(str
, len
- 1); /* strip trailing 's' */
666 rc
= strtosize(str
, res
);
678 void resize_partition(struct fdisk_context
*cxt
)
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
;
689 rc
= fdisk_ask_partnum(cxt
, &i
, FALSE
);
693 rc
= fdisk_partition_get_max_size(cxt
, i
, &max_size
);
697 max_size
*= fdisk_get_sector_size(cxt
);
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)"),
704 rc
= fdisk_ask_string(cxt
, query
, &response
);
709 rc
= strtosize_sectors(response
, fdisk_get_sector_size(cxt
), &size
);
710 if (rc
|| size
> max_size
) {
711 fdisk_warnx(cxt
, _("Invalid size"));
715 npa
= fdisk_new_partition();
719 secs
= size
/ fdisk_get_sector_size(cxt
);
720 fdisk_partition_size_explicit(npa
, 1);
721 fdisk_partition_set_size(npa
, secs
);
723 rc
= fdisk_set_partition(cxt
, i
, npa
);
727 fdisk_info(cxt
, _("Partition %zu has been resized."), i
+ 1);
732 fdisk_unref_partition(next
);
733 fdisk_unref_partition(pa
);
734 fdisk_unref_partition(npa
);
735 fdisk_unref_table(tb
);
739 fdisk_warnx(cxt
, _("Could not resize partition %zu: %s"),
740 i
+ 1, strerror(-rc
));
744 void change_partition_type(struct fdisk_context
*cxt
)
747 struct fdisk_parttype
*t
= NULL
;
748 struct fdisk_partition
*pa
= NULL
;
749 const char *old
= NULL
;
754 if (fdisk_ask_partnum(cxt
, &i
, FALSE
))
757 if (fdisk_get_partition(cxt
, i
, &pa
)) {
758 fdisk_warnx(cxt
, _("Partition %zu does not exist yet!"), i
+ 1);
762 t
= (struct fdisk_parttype
*) fdisk_partition_get_type(pa
);
763 old
= t
? fdisk_parttype_get_name(t
) : _("Unknown");
766 t
= ask_partition_type(cxt
, &canceled
);
771 if (canceled
== 0 && t
&& fdisk_set_partition_type(cxt
, i
, t
) == 0)
773 _("Changed type of partition '%s' to '%s'."),
774 old
, t
? fdisk_parttype_get_name(t
) : _("Unknown"));
777 _("Type of partition %zu is unchanged: %s."),
780 fdisk_unref_partition(pa
);
781 fdisk_unref_parttype(t
);
786 static int do_discard(struct fdisk_context
*cxt
, struct fdisk_partition
*pa
)
793 ss
= fdisk_get_sector_size(cxt
);
795 range
[0] = (uint64_t) fdisk_partition_get_start(pa
);
796 range
[1] = (uint64_t) fdisk_partition_get_size(pa
);
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);
802 range
[0] *= (uint64_t) ss
;
803 range
[1] *= (uint64_t) ss
;
805 fdisk_ask_yesno(cxt
, buf
, &yes
);
810 if (ioctl(fdisk_get_devfd(cxt
), BLKDISCARD
, &range
)) {
811 fdisk_warn(cxt
, _("BLKDISCARD ioctl failed"));
817 static void discard_partition(struct fdisk_context
*cxt
)
819 struct fdisk_partition
*pa
= NULL
;
822 fdisk_info(cxt
, _("\nThe partition sectors will be immediately discarded.\n"
823 "You can exit this dialog by pressing CTRL+C.\n"));
825 if (fdisk_ask_partnum(cxt
, &n
, FALSE
))
827 if (fdisk_get_partition(cxt
, n
, &pa
)) {
828 fdisk_warnx(cxt
, _("Partition %zu does not exist yet!"), n
+ 1);
832 if (!fdisk_partition_has_size(pa
) || !fdisk_partition_has_start(pa
)) {
833 fdisk_warnx(cxt
, _("Partition %zu has an unspecified range."), n
+ 1);
837 if (do_discard(cxt
, pa
) == 0)
838 fdisk_info(cxt
, _("Discarded sectors on partition %zu."), n
+ 1);
840 fdisk_unref_partition(pa
);
843 static void discard_freespace(struct fdisk_context
*cxt
)
845 struct fdisk_partition
*pa
= NULL
;
846 struct fdisk_table
*tb
= NULL
;
851 ct
= list_freespace_get_table(cxt
, &tb
, &best
);
853 fdisk_info(cxt
, _("No free space."));
856 fdisk_info(cxt
, _("\nThe unused sectors will be immediately discarded.\n"
857 "You can exit this dialog by pressing CTRL+C.\n"));
859 if (fdisk_ask_number(cxt
, 1, best
+ 1, (uintmax_t) ct
,
860 _("Free space number"), &n
) != 0)
863 pa
= fdisk_table_get_partition(tb
, n
- 1);
867 if (!fdisk_partition_has_size(pa
) || !fdisk_partition_has_start(pa
)) {
868 fdisk_warnx(cxt
, _("Free space %"PRIu64
"has an unspecified range"), n
);
872 if (do_discard(cxt
, pa
) == 0)
873 fdisk_info(cxt
, _("Discarded sectors on free space."));
875 fdisk_unref_table(tb
);
878 void discard_sectors(struct fdisk_context
*cxt
)
882 if (fdisk_is_readonly(cxt
)) {
883 fdisk_warnx(cxt
, _("Discarding sectors is not possible in read-only mode."));
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)
894 discard_partition(cxt
);
897 discard_freespace(cxt
);
902 #else /* !BLKDISCARD */
903 void discard_sectors(struct fdisk_context
*cxt
)
905 fdisk_warnx(cxt
, _("Discard unsupported on your system."));
907 #endif /* BLKDISCARD */
909 int print_partition_info(struct fdisk_context
*cxt
)
911 struct fdisk_partition
*pa
= NULL
;
915 struct fdisk_label
*lb
= fdisk_get_label(cxt
, NULL
);
917 if ((rc
= fdisk_ask_partnum(cxt
, &i
, FALSE
)))
920 if ((rc
= fdisk_get_partition(cxt
, i
, &pa
))) {
921 fdisk_warnx(cxt
, _("Partition %zu does not exist yet!"), i
+ 1);
925 if ((rc
= fdisk_label_get_fields_ids_all(lb
, cxt
, &fields
, &nfields
)))
928 for (i
= 0; i
< nfields
; ++i
) {
931 const struct fdisk_field
*fd
= fdisk_label_get_field(lb
, id
);
936 rc
= fdisk_partition_to_string(pa
, cxt
, id
, &data
);
941 fdisk_info(cxt
, "%15s: %s", fdisk_field_get_name(fd
), data
);
946 fdisk_unref_partition(pa
);
951 static size_t skip_empty(const unsigned char *buf
, size_t i
, size_t sz
)
954 const unsigned char *p0
= buf
+ i
;
956 for (next
= i
+ 16; next
< sz
; next
+= 16) {
957 if (memcmp(p0
, buf
+ next
, 16) != 0)
961 return next
== i
+ 16 ? i
: next
;
964 static void dump_buffer(off_t base
, unsigned char *buf
, size_t sz
)
966 size_t i
, l
, next
= 0;
970 for (i
= 0, l
= 0; i
< sz
; i
++, l
++) {
973 next
= skip_empty(buf
, i
, sz
);
974 printf("%08jx ", (intmax_t)base
+ i
);
976 printf(" %02x", buf
[i
]);
977 if (l
== 7) /* words separator */
980 fputc('\n', stdout
); /* next line */
993 static void dump_blkdev(struct fdisk_context
*cxt
, const char *name
,
994 uint64_t offset
, size_t size
)
996 int fd
= fdisk_get_devfd(cxt
);
998 fdisk_info(cxt
, _("\n%s: offset = %"PRIu64
", size = %zu bytes."),
1003 if (lseek(fd
, (off_t
) offset
, SEEK_SET
) == (off_t
) -1)
1004 fdisk_warn(cxt
, _("cannot seek"));
1006 unsigned char *buf
= xmalloc(size
);
1008 if (read_all(fd
, (char *) buf
, size
) != (ssize_t
) size
)
1009 fdisk_warn(cxt
, _("cannot read"));
1011 dump_buffer(offset
, buf
, size
);
1016 void dump_firstsector(struct fdisk_context
*cxt
)
1020 dump_blkdev(cxt
, _("First sector"), 0, fdisk_get_sector_size(cxt
));
1023 void dump_disklabel(struct fdisk_context
*cxt
)
1026 const char *name
= NULL
;
1027 uint64_t offset
= 0;
1032 while (fdisk_locate_disklabel(cxt
, i
++, &name
, &offset
, &size
) == 0 && size
)
1033 dump_blkdev(cxt
, name
, offset
, size
);
1036 static fdisk_sector_t
get_dev_blocks(char *dev
)
1039 fdisk_sector_t size
;
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
);
1046 err(EXIT_FAILURE
, _("BLKGETSIZE ioctl failed on %s"), dev
);
1051 void follow_wipe_mode(struct fdisk_context
*cxt
)
1053 int dowipe
= wipemode
== WIPEMODE_ALWAYS
? 1 : 0;
1055 if (isatty(STDIN_FILENO
) && wipemode
== WIPEMODE_AUTO
)
1056 dowipe
= 1; /* do it in interactive mode */
1058 if (fdisk_is_ptcollision(cxt
) && wipemode
!= WIPEMODE_NEVER
)
1059 dowipe
= 1; /* always remove old PT */
1061 fdisk_enable_wipe(cxt
, dowipe
);
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
));
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
));
1075 static void __attribute__((__noreturn__
)) usage(void)
1079 fputs(USAGE_HEADER
, 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
);
1086 fputs(USAGE_SEPARATOR
, out
);
1087 fputs(_("Display or manipulate a disk partition table.\n"), out
);
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
);
1094 _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
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
);
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
);
1107 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
1109 _(" -w, --wipe <mode> wipe signatures (%s, %s or %s)\n"), "auto", "always", "never");
1111 _(" -W, --wipe-partitions <mode> wipe signatures from new partitions (%s, %s or %s)\n"), "auto", "always", "never");
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
);
1118 fputs(USAGE_SEPARATOR
, out
);
1119 fprintf(out
, USAGE_HELP_OPTIONS(31));
1121 list_available_columns(out
);
1123 fprintf(out
, USAGE_MAN_TAIL("fdisk(8)"));
1129 ACT_FDISK
= 0, /* default */
1135 int main(int argc
, char **argv
)
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
;
1143 OPT_BYTES
= CHAR_MAX
+ 1,
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 }
1170 setlocale(LC_ALL
, "");
1171 bindtextdomain(PACKAGE
, LOCALEDIR
);
1172 textdomain(PACKAGE
);
1173 close_stdout_atexit();
1175 fdisk_init_debug(0);
1176 scols_init_debug(0);
1177 fdiskprog_init_debug();
1179 cxt
= fdisk_new_context();
1181 err(EXIT_FAILURE
, _("failed to allocate libfdisk context"));
1183 fdisk_set_ask(cxt
, ask_callback
, NULL
);
1185 while ((c
= getopt_long(argc
, argv
, "b:Bc::C:hH:lL::no:sS:t:u::vVw:W:x",
1186 longopts
, NULL
)) != -1) {
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
);
1198 fdisk_enable_bootbits_protection(cxt
, 1);
1201 fdisk_save_user_geometry(cxt
,
1202 strtou32_or_err(optarg
,
1203 _("invalid cylinders argument")),
1208 /* this setting is independent on the current
1209 * actively used label
1211 char *p
= *optarg
== '=' ? optarg
+ 1 : optarg
;
1212 struct fdisk_label
*lb
= fdisk_get_label(cxt
, "dos");
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
);
1221 errx(EXIT_FAILURE
, _("unknown compatibility mode '%s'"), p
);
1223 /* use default if no optarg specified */
1226 fdisk_save_user_geometry(cxt
, 0,
1227 strtou32_or_err(optarg
,
1228 _("invalid heads argument")),
1232 fdisk_save_user_geometry(cxt
, 0, 0,
1233 strtou32_or_err(optarg
,
1234 _("invalid sectors argument")));
1240 act
= ACT_LIST_DETAILS
;
1243 colormode
= UL_COLORMODE_AUTO
;
1245 colormode
= colormode_or_err(optarg
,
1246 _("unsupported color mode"));
1259 struct fdisk_label
*lb
= NULL
;
1261 while (fdisk_next_label(cxt
, &lb
) == 0)
1262 fdisk_label_set_disabled(lb
, 1);
1264 lb
= fdisk_get_label(cxt
, optarg
);
1266 errx(EXIT_FAILURE
, _("unsupported disklabel: %s"), optarg
);
1267 fdisk_label_set_disabled(lb
, 0);
1271 if (optarg
&& *optarg
== '=')
1273 if (fdisk_set_unit(cxt
, optarg
) != 0)
1274 errx(EXIT_FAILURE
, _("unsupported unit"));
1276 case 'V': /* preferred for util-linux */
1277 case 'v': /* for backward compatibility only */
1278 print_version(EXIT_SUCCESS
);
1280 wipemode
= wipemode_from_string(optarg
);
1282 errx(EXIT_FAILURE
, _("unsupported wipe mode"));
1285 pwipemode
= wipemode_from_string(optarg
);
1287 errx(EXIT_FAILURE
, _("unsupported wipe mode"));
1292 fdisk_set_size_unit(cxt
, FDISK_SIZEUNIT_BYTES
);
1303 errtryhelp(EXIT_FAILURE
);
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."));
1311 colors_init(colormode
, "fdisk");
1312 is_interactive
= isatty(STDIN_FILENO
);
1316 case ACT_LIST_DETAILS
:
1317 fdisk_enable_listonly(cxt
, 1);
1319 if (act
== ACT_LIST_DETAILS
)
1320 fdisk_enable_details(cxt
, 1);
1322 init_fields(cxt
, outarg
, NULL
);
1324 if (argc
> optind
) {
1327 for (rc
= 0, k
= optind
; k
< argc
; k
++)
1328 rc
+= print_device_pt(cxt
, argv
[k
], 1, 0, k
!= optind
);
1331 return EXIT_FAILURE
;
1333 print_all_devices_pt(cxt
, 0);
1338 if (argc
- optind
<= 0) {
1339 warnx(_("bad usage"));
1340 errtryhelp(EXIT_FAILURE
);
1342 for (i
= optind
; i
< argc
; i
++) {
1343 uintmax_t blks
= get_dev_blocks(argv
[i
]);
1345 if (argc
- optind
== 1)
1346 printf("%ju\n", blks
);
1348 printf("%s: %ju\n", argv
[i
], blks
);
1353 if (argc
-optind
!= 1) {
1354 warnx(_("bad usage"));
1355 errtryhelp(EXIT_FAILURE
);
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
);
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"));
1365 devname
= argv
[optind
];
1366 rc
= fdisk_assign_device(cxt
, devname
, 0);
1367 if (rc
== -EACCES
) {
1368 rc
= fdisk_assign_device(cxt
, devname
, 1);
1370 fdisk_warnx(cxt
, _("Device is open in read-only mode."));
1373 err(EXIT_FAILURE
, _("cannot open %s"), devname
);
1375 if (fdisk_device_is_used(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"));
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
;
1390 if (fdisk_get_collision(cxt
))
1391 follow_wipe_mode(cxt
);
1393 if (!fdisk_has_label(cxt
)) {
1394 fdisk_info(cxt
, _("Device does not contain a recognized partition table."));
1396 fdisk_create_disklabel(cxt
, NULL
);
1398 } else if (fdisk_is_label(cxt
, GPT
) && fdisk_gpt_is_hybrid(cxt
))
1400 "A hybrid GPT was detected. You have to sync "
1401 "the hybrid MBR manually (expert command 'M')."));
1403 init_fields(cxt
, outarg
, NULL
); /* -o <columns> */
1405 if (!fdisk_is_readonly(cxt
)) {
1406 fdisk_get_partitions(cxt
, &original_layout
);
1407 device_is_used
= fdisk_device_is_used(cxt
);
1411 process_fdisk_menu(&cxt
);
1414 fdisk_unref_context(cxt
);
1415 return EXIT_SUCCESS
;