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>
63 # define BLKDISCARD _IO(0x12,119)
67 int pwipemode
= WIPEMODE_AUTO
;
70 struct fdisk_table
*original_layout
;
72 static int wipemode
= WIPEMODE_AUTO
;
75 * fdisk debug stuff (see fdisk.h and include/debug.h)
77 UL_DEBUG_DEFINE_MASK(fdisk
);
78 UL_DEBUG_DEFINE_MASKNAMES(fdisk
) = UL_DEBUG_EMPTY_MASKNAMES
;
80 static void fdiskprog_init_debug(void)
82 __UL_INIT_DEBUG_FROM_ENV(fdisk
, FDISKPROG_DEBUG_
, 0, FDISK_DEBUG
);
85 static void reply_sighandler(int sig
__attribute__((unused
)))
87 DBG(ASK
, ul_debug("got signal"));
90 static int reply_running
;
92 #ifdef HAVE_LIBREADLINE
93 static char *reply_line
;
95 static void reply_linehandler(char *line
)
99 rl_callback_handler_remove(); /* avoid duplicate prompt */
103 int get_user_reply(const char *prompt
, char *buf
, size_t bufsz
)
105 struct sigaction oldact
, act
= {
106 .sa_handler
= reply_sighandler
108 struct pollfd fds
[] = {
109 { .fd
= fileno(stdin
), .events
= POLLIN
}
114 DBG(ASK
, ul_debug("asking for user reply %s", is_interactive
? "[interactive]" : ""));
116 sigemptyset(&act
.sa_mask
);
117 sigaction(SIGINT
, &act
, &oldact
);
119 #ifdef HAVE_LIBREADLINE
121 rl_callback_handler_install(prompt
, reply_linehandler
);
129 #ifdef HAVE_LIBREADLINE
133 fputs(prompt
, stdout
);
137 rc
= poll(fds
, 1, -1);
138 if (rc
== -1 && errno
== EINTR
) { /* interrupted by signal */
139 DBG(ASK
, ul_debug("cancel by CTRL+C"));
143 if (rc
== -1 && errno
!= EAGAIN
) { /* error */
147 #ifdef HAVE_LIBREADLINE
148 if (is_interactive
) {
149 /* read input and copy to buf[] */
150 rl_callback_read_char();
151 if (!reply_running
&& reply_line
) {
152 sz
= strlen(reply_line
);
156 memcpy(buf
, reply_line
, min(sz
, bufsz
));
157 buf
[min(sz
, bufsz
- 1)] = '\0';
164 if (!fgets(buf
, bufsz
, stdin
))
168 } while (reply_running
);
171 DBG(ASK
, ul_debug("cancel by CTRL+D"));
180 sz
= ltrim_whitespace((unsigned char *) buf
);
181 if (sz
&& *(buf
+ sz
- 1) == '\n')
182 *(buf
+ sz
- 1) = '\0';
185 #ifdef HAVE_LIBREADLINE
187 rl_callback_handler_remove();
189 sigaction(SIGINT
, &oldact
, NULL
);
190 DBG(ASK
, ul_debug("user's reply: >>>%s<<< [rc=%d]", buf
, ret
));
194 static int ask_menu(struct fdisk_context
*cxt
, struct fdisk_ask
*ask
,
195 char *buf
, size_t bufsz
)
198 const char *q
= fdisk_ask_get_query(ask
);
199 int dft
= fdisk_ask_menu_get_default(ask
);
202 fputs(q
, stdout
); /* print header */
209 const char *name
, *desc
;
212 /* print menu items */
213 while (fdisk_ask_menu_get_item(ask
, i
++, &key
, &name
, &desc
) == 0) {
215 fprintf(stdout
, " %c %s (%s)\n", key
, name
, desc
);
217 fprintf(stdout
, " %c %s\n", key
, name
);
221 snprintf(prompt
, sizeof(prompt
), _("Select (default %c): "), dft
);
222 rc
= get_user_reply(prompt
, buf
, bufsz
);
226 fdisk_info(cxt
, _("Using default response %c."), dft
);
233 while (fdisk_ask_menu_get_item(ask
, i
++, &key
, NULL
, NULL
) == 0) {
235 fdisk_ask_menu_set_result(ask
, c
);
236 return 0; /* success */
239 fdisk_warnx(cxt
, _("Value out of range."));
246 #define tochar(num) ((int) ('a' + num - 1))
247 static int ask_number(struct fdisk_context
*cxt
,
248 struct fdisk_ask
*ask
,
249 char *buf
, size_t bufsz
)
251 char prompt
[128] = { '\0' };
252 const char *q
= fdisk_ask_get_query(ask
);
253 const char *range
= fdisk_ask_number_get_range(ask
);
255 uint64_t dflt
= fdisk_ask_number_get_default(ask
),
256 low
= fdisk_ask_number_get_low(ask
),
257 high
= fdisk_ask_number_get_high(ask
);
258 int inchar
= fdisk_ask_number_inchars(ask
);
262 DBG(ASK
, ul_debug("asking for number "
263 "['%s', <%"PRIu64
",%"PRIu64
">, default=%"PRIu64
", range: %s]",
264 q
, low
, high
, dflt
, range
));
266 if (range
&& dflt
>= low
&& dflt
<= high
) {
268 snprintf(prompt
, sizeof(prompt
), _("%s (%s, default %c): "),
269 q
, range
, tochar(dflt
));
271 snprintf(prompt
, sizeof(prompt
), _("%s (%s, default %"PRIu64
"): "),
274 } else if (dflt
>= low
&& dflt
<= high
) {
276 snprintf(prompt
, sizeof(prompt
), _("%s (%c-%c, default %c): "),
277 q
, tochar(low
), tochar(high
), tochar(dflt
));
279 snprintf(prompt
, sizeof(prompt
),
280 _("%s (%"PRIu64
"-%"PRIu64
", default %"PRIu64
"): "),
283 snprintf(prompt
, sizeof(prompt
), _("%s (%c-%c): "),
284 q
, tochar(low
), tochar(high
));
286 snprintf(prompt
, sizeof(prompt
), _("%s (%"PRIu64
"-%"PRIu64
"): "),
290 int rc
= get_user_reply(prompt
, buf
, bufsz
);
295 if (!*buf
&& dflt
>= low
&& dflt
<= high
)
296 return fdisk_ask_number_set_result(ask
, dflt
);
298 if (isdigit_string(buf
)) {
302 num
= strtoumax(buf
, &end
, 10);
303 if (errno
|| buf
== end
|| (end
&& *end
))
305 } else if (inchar
&& isalpha(*buf
)) {
306 num
= tolower(*buf
) - 'a' + 1;
310 if (rc
== 0 && num
>= low
&& num
<= high
)
311 return fdisk_ask_number_set_result(ask
, num
);
313 fdisk_warnx(cxt
, _("Value out of range."));
319 static int ask_offset(struct fdisk_context
*cxt
,
320 struct fdisk_ask
*ask
,
321 char *buf
, size_t bufsz
)
323 char prompt
[128] = { '\0' };
324 const char *q
= fdisk_ask_get_query(ask
);
325 const char *range
= fdisk_ask_number_get_range(ask
);
327 uint64_t dflt
= fdisk_ask_number_get_default(ask
),
328 low
= fdisk_ask_number_get_low(ask
),
329 high
= fdisk_ask_number_get_high(ask
),
330 base
= fdisk_ask_number_get_base(ask
);
334 DBG(ASK
, ul_debug("asking for offset ['%s', <%"PRIu64
",%"PRIu64
">, base=%"PRIu64
", default=%"PRIu64
", range: %s]",
335 q
, low
, high
, base
, dflt
, range
));
337 if (range
&& dflt
>= low
&& dflt
<= high
)
338 snprintf(prompt
, sizeof(prompt
), _("%s (%s, default %"PRIu64
"): "),
340 else if (dflt
>= low
&& dflt
<= high
)
341 snprintf(prompt
, sizeof(prompt
),
342 _("%s (%"PRIu64
"-%"PRIu64
", default %"PRIu64
"): "),
345 snprintf(prompt
, sizeof(prompt
), _("%s (%"PRIu64
"-%"PRIu64
"): "),
353 int rc
= get_user_reply(prompt
, buf
, bufsz
);
356 if (!*buf
&& dflt
>= low
&& dflt
<= high
)
357 return fdisk_ask_number_set_result(ask
, dflt
);
360 if (*p
== '+' || *p
== '-') {
365 rc
= parse_size(p
, &num
, &pwr
);
368 DBG(ASK
, ul_debug("parsed size: %ju", num
));
370 /* +{size}{K,M,...} specified, the "num" is in bytes */
371 uint64_t unit
= fdisk_ask_number_get_unit(ask
);
372 num
+= unit
/2; /* round */
377 else if (sig
== '-' && fdisk_ask_number_is_wrap_negative(ask
))
382 DBG(ASK
, ul_debug("final offset: %ju [sig: %c, power: %d, %s]",
384 sig
? "relative" : "absolute"));
385 if (num
>= low
&& num
<= high
) {
387 fdisk_ask_number_set_relative(ask
, 1);
388 return fdisk_ask_number_set_result(ask
, (uint64_t)num
);
390 fdisk_warnx(cxt
, _("Value out of range."));
396 static unsigned int info_count
;
398 static void fputs_info(struct fdisk_ask
*ask
, FILE *out
)
403 msg
= fdisk_ask_print_get_mesg(ask
);
413 int ask_callback(struct fdisk_context
*cxt
, struct fdisk_ask
*ask
,
414 void *data
__attribute__((__unused__
)))
417 char buf
[BUFSIZ
] = { '\0' };
422 if (fdisk_ask_get_type(ask
) != FDISK_ASKTYPE_INFO
)
425 switch(fdisk_ask_get_type(ask
)) {
426 case FDISK_ASKTYPE_MENU
:
427 return ask_menu(cxt
, ask
, buf
, sizeof(buf
));
428 case FDISK_ASKTYPE_NUMBER
:
429 return ask_number(cxt
, ask
, buf
, sizeof(buf
));
430 case FDISK_ASKTYPE_OFFSET
:
431 return ask_offset(cxt
, ask
, buf
, sizeof(buf
));
432 case FDISK_ASKTYPE_INFO
:
433 if (!fdisk_is_listonly(cxt
))
435 fputs_info(ask
, stdout
);
437 case FDISK_ASKTYPE_WARNX
:
439 color_scheme_fenable("warn", UL_COLOR_RED
, stderr
);
440 fputs(fdisk_ask_print_get_mesg(ask
), stderr
);
441 color_fdisable(stderr
);
444 case FDISK_ASKTYPE_WARN
:
446 color_scheme_fenable("warn", UL_COLOR_RED
, stderr
);
447 fputs(fdisk_ask_print_get_mesg(ask
), stderr
);
448 errno
= fdisk_ask_print_get_errno(ask
);
449 fprintf(stderr
, ": %m\n");
450 color_fdisable(stderr
);
452 case FDISK_ASKTYPE_YESNO
:
456 fputs(fdisk_ask_get_query(ask
), stdout
);
457 rc
= get_user_reply(_(" [Y]es/[N]o: "), buf
, sizeof(buf
));
461 if (x
== RPMATCH_YES
|| x
== RPMATCH_NO
) {
462 fdisk_ask_yesno_set_result(ask
, x
);
466 DBG(ASK
, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf
, rc
));
468 case FDISK_ASKTYPE_STRING
:
471 snprintf(prmt
, sizeof(prmt
), "%s: ", fdisk_ask_get_query(ask
));
473 rc
= get_user_reply(prmt
, buf
, sizeof(buf
));
475 fdisk_ask_string_set_result(ask
, xstrdup(buf
));
476 DBG(ASK
, ul_debug("string ask: reply '%s' [rc=%d]", buf
, rc
));
480 warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask
));
486 static struct fdisk_parttype
*ask_partition_type(struct fdisk_context
*cxt
, int *canceled
)
489 struct fdisk_label
*lb
;
492 lb
= fdisk_get_label(cxt
, NULL
);
499 if (fdisk_label_has_parttypes_shortcuts(lb
))
500 q
= fdisk_label_has_code_parttypes(lb
) ?
501 _("Hex code or alias (type L to list all): ") :
502 _("Partition type or alias (type L to list all): ");
504 q
= fdisk_label_has_code_parttypes(lb
) ?
505 _("Hex code (type L to list all codes): ") :
506 _("Partition type (type L to list all types): ");
508 char buf
[256] = { '\0' };
509 int rc
= get_user_reply(q
, buf
, sizeof(buf
));
512 if (rc
== -ECANCELED
)
517 if (buf
[1] == '\0' && toupper(*buf
) == 'L')
518 list_partition_types(cxt
);
520 struct fdisk_parttype
*t
= fdisk_label_advparse_parttype(lb
, buf
,
521 FDISK_PARTTYPE_PARSE_DATA
522 | FDISK_PARTTYPE_PARSE_ALIAS
523 | FDISK_PARTTYPE_PARSE_NAME
524 | FDISK_PARTTYPE_PARSE_SEQNUM
);
526 fdisk_info(cxt
, _("Failed to parse '%s' partition type."), buf
);
535 void list_partition_types(struct fdisk_context
*cxt
)
537 size_t ntypes
= 0, next
= 0;
538 struct fdisk_label
*lb
;
542 lb
= fdisk_get_label(cxt
, NULL
);
545 ntypes
= fdisk_label_get_nparttypes(lb
);
549 if (fdisk_label_has_code_parttypes(lb
)) {
551 * Prints in 4 columns in format <hex> <name>
553 size_t last
[4], done
= 0, size
;
558 for (i
= 3; i
>= 0; i
--)
559 last
[3 - i
] = done
+= (size
+ i
- done
) / (i
+ 1);
563 #define NAME_WIDTH 15
564 char name
[NAME_WIDTH
* MB_LEN_MAX
];
565 size_t width
= NAME_WIDTH
;
566 const struct fdisk_parttype
*t
= fdisk_label_get_parttype(lb
, next
);
569 if (fdisk_parttype_get_name(t
)) {
570 printf("%s%02x ", i
? " " : "\n",
571 fdisk_parttype_get_code(t
));
572 ret
= mbsalign(_(fdisk_parttype_get_name(t
)),
574 &width
, MBS_ALIGN_LEFT
, 0);
576 if (ret
== (size_t)-1 || ret
>= sizeof(name
))
578 _(fdisk_parttype_get_name(t
)));
583 next
= last
[i
++] + done
;
584 if (i
> 3 || next
>= last
[i
]) {
588 } while (done
< last
[0]);
593 * Prints 1 column in format <idx> <name> <typestr>
600 for (i
= 0; i
< ntypes
; i
++) {
601 const struct fdisk_parttype
*t
= fdisk_label_get_parttype(lb
, i
);
602 printf("%3zu %-30s %s\n", i
+ 1,
603 fdisk_parttype_get_name(t
),
604 fdisk_parttype_get_string(t
));
613 if (fdisk_label_has_parttypes_shortcuts(lb
)) {
614 const char *alias
= NULL
, *typestr
= NULL
;
617 fputs(_("\nAliases:\n"), stdout
);
619 for (next
= 0; rc
== 0 || rc
== 2; next
++) {
620 /* rc: <0 error, 0 success, 1 end, 2 deprecated */
621 rc
= fdisk_label_get_parttype_shortcut(lb
,
622 next
, &typestr
, NULL
, &alias
);
624 printf(" %-14s - %s\n", alias
, typestr
);
633 void toggle_dos_compatibility_flag(struct fdisk_context
*cxt
)
635 struct fdisk_label
*lb
= fdisk_get_label(cxt
, "dos");
641 flag
= !fdisk_dos_is_compatible(lb
);
642 fdisk_info(cxt
, flag
?
643 _("DOS Compatibility flag is set (DEPRECATED!)") :
644 _("DOS Compatibility flag is not set"));
646 fdisk_dos_enable_compatible(lb
, flag
);
648 if (fdisk_is_label(cxt
, DOS
))
649 fdisk_reset_alignment(cxt
); /* reset the current label */
652 static int strtosize_sectors(const char *str
, unsigned long sector_size
,
655 size_t len
= strlen(str
);
662 if (str
[len
- 1] == 'S' || str
[len
- 1] == 's') {
664 str
= strndup(str
, len
- 1); /* strip trailing 's' */
669 rc
= strtosize(str
, res
);
681 void resize_partition(struct fdisk_context
*cxt
)
683 struct fdisk_partition
*pa
= NULL
, *npa
= NULL
, *next
= NULL
;
684 char *query
= NULL
, *response
= NULL
, *default_size
;
685 struct fdisk_table
*tb
= NULL
;
686 uint64_t max_size
, size
, secs
;
692 rc
= fdisk_ask_partnum(cxt
, &i
, FALSE
);
696 rc
= fdisk_partition_get_max_size(cxt
, i
, &max_size
);
700 max_size
*= fdisk_get_sector_size(cxt
);
702 default_size
= size_to_human_string(0, max_size
);
703 xasprintf(&query
, _("New <size>{K,M,G,T,P} in bytes or <size>S in sectors (default %s)"),
707 rc
= fdisk_ask_string(cxt
, query
, &response
);
712 rc
= strtosize_sectors(response
, fdisk_get_sector_size(cxt
), &size
);
713 if (rc
|| size
> max_size
) {
714 fdisk_warnx(cxt
, _("Invalid size"));
718 npa
= fdisk_new_partition();
722 secs
= size
/ fdisk_get_sector_size(cxt
);
723 fdisk_partition_size_explicit(npa
, 1);
724 fdisk_partition_set_size(npa
, secs
);
726 rc
= fdisk_set_partition(cxt
, i
, npa
);
730 fdisk_info(cxt
, _("Partition %zu has been resized."), i
+ 1);
735 fdisk_unref_partition(next
);
736 fdisk_unref_partition(pa
);
737 fdisk_unref_partition(npa
);
738 fdisk_unref_table(tb
);
742 fdisk_warnx(cxt
, _("Could not resize partition %zu: %s"),
743 i
+ 1, strerror(-rc
));
747 void change_partition_type(struct fdisk_context
*cxt
)
750 struct fdisk_parttype
*t
= NULL
;
751 struct fdisk_partition
*pa
= NULL
;
752 const char *old
= NULL
;
757 if (fdisk_ask_partnum(cxt
, &i
, FALSE
))
760 if (fdisk_get_partition(cxt
, i
, &pa
)) {
761 fdisk_warnx(cxt
, _("Partition %zu does not exist yet!"), i
+ 1);
765 t
= (struct fdisk_parttype
*) fdisk_partition_get_type(pa
);
766 old
= t
? fdisk_parttype_get_name(t
) : _("Unknown");
769 t
= ask_partition_type(cxt
, &canceled
);
774 if (canceled
== 0 && t
&& fdisk_set_partition_type(cxt
, i
, t
) == 0)
776 _("Changed type of partition '%s' to '%s'."),
777 old
, t
? fdisk_parttype_get_name(t
) : _("Unknown"));
780 _("Type of partition %zu is unchanged: %s."),
783 fdisk_unref_partition(pa
);
784 fdisk_unref_parttype(t
);
789 static int do_discard(struct fdisk_context
*cxt
, struct fdisk_partition
*pa
)
796 ss
= fdisk_get_sector_size(cxt
);
798 range
[0] = (uint64_t) fdisk_partition_get_start(pa
);
799 range
[1] = (uint64_t) fdisk_partition_get_size(pa
);
801 snprintf(buf
, sizeof(buf
), _("All data in the region (%"PRIu64
802 "-%"PRIu64
") will be lost! Continue?"),
803 range
[0], range
[0] + range
[1] - 1);
805 range
[0] *= (uint64_t) ss
;
806 range
[1] *= (uint64_t) ss
;
808 fdisk_ask_yesno(cxt
, buf
, &yes
);
813 if (ioctl(fdisk_get_devfd(cxt
), BLKDISCARD
, &range
)) {
814 fdisk_warn(cxt
, _("BLKDISCARD ioctl failed"));
820 static void discard_partition(struct fdisk_context
*cxt
)
822 struct fdisk_partition
*pa
= NULL
;
825 fdisk_info(cxt
, _("\nThe partition sectors will be immediately discarded.\n"
826 "You can exit this dialog by pressing CTRL+C.\n"));
828 if (fdisk_ask_partnum(cxt
, &n
, FALSE
))
830 if (fdisk_get_partition(cxt
, n
, &pa
)) {
831 fdisk_warnx(cxt
, _("Partition %zu does not exist yet!"), n
+ 1);
835 if (!fdisk_partition_has_size(pa
) || !fdisk_partition_has_start(pa
)) {
836 fdisk_warnx(cxt
, _("Partition %zu has unspeficied range"), n
+ 1);
840 if (do_discard(cxt
, pa
) == 0)
841 fdisk_info(cxt
, _("Discarded sectors on partition %zu."), n
+ 1);
843 fdisk_unref_partition(pa
);
846 static void discard_freespace(struct fdisk_context
*cxt
)
848 struct fdisk_partition
*pa
= NULL
;
849 struct fdisk_table
*tb
= NULL
;
854 ct
= list_freespace_get_table(cxt
, &tb
, &best
);
856 fdisk_info(cxt
, _("No free space."));
859 fdisk_info(cxt
, _("\nThe unused sectors will be immediately discarded.\n"
860 "You can exit this dialog by pressing CTRL+C.\n"));
862 if (fdisk_ask_number(cxt
, 1, best
+ 1, (uintmax_t) ct
,
863 _("Free space number"), &n
) != 0)
866 pa
= fdisk_table_get_partition(tb
, n
- 1);
870 if (!fdisk_partition_has_size(pa
) || !fdisk_partition_has_start(pa
)) {
871 fdisk_warnx(cxt
, _("Free space %"PRIu64
"has unspeficied range"), n
);
875 if (do_discard(cxt
, pa
) == 0)
876 fdisk_info(cxt
, _("Discarded sectors on free space."));
878 fdisk_unref_table(tb
);
881 void discard_sectors(struct fdisk_context
*cxt
)
885 if (fdisk_is_readonly(cxt
)) {
886 fdisk_warnx(cxt
, _("Discarding sectors is not possible in read-only mode."));
890 if (fdisk_ask_menu(cxt
, _("Type of area to be discarded"),
891 &c
, 'p', _("partition sectors"), 'p',
892 _("free space sectros"), 'f', NULL
) != 0)
897 discard_partition(cxt
);
900 discard_freespace(cxt
);
905 #else /* !BLKDISCARD */
906 void discard_sectors(struct fdisk_context
*cxt
)
908 fdisk_warnx(cxt
, _("Discard unsupported on your system."));
910 #endif /* BLKDISCARD */
912 int print_partition_info(struct fdisk_context
*cxt
)
914 struct fdisk_partition
*pa
= NULL
;
918 struct fdisk_label
*lb
= fdisk_get_label(cxt
, NULL
);
920 if ((rc
= fdisk_ask_partnum(cxt
, &i
, FALSE
)))
923 if ((rc
= fdisk_get_partition(cxt
, i
, &pa
))) {
924 fdisk_warnx(cxt
, _("Partition %zu does not exist yet!"), i
+ 1);
928 if ((rc
= fdisk_label_get_fields_ids_all(lb
, cxt
, &fields
, &nfields
)))
931 for (i
= 0; i
< nfields
; ++i
) {
934 const struct fdisk_field
*fd
= fdisk_label_get_field(lb
, id
);
939 rc
= fdisk_partition_to_string(pa
, cxt
, id
, &data
);
944 fdisk_info(cxt
, "%15s: %s", fdisk_field_get_name(fd
), data
);
949 fdisk_unref_partition(pa
);
954 static size_t skip_empty(const unsigned char *buf
, size_t i
, size_t sz
)
957 const unsigned char *p0
= buf
+ i
;
959 for (next
= i
+ 16; next
< sz
; next
+= 16) {
960 if (memcmp(p0
, buf
+ next
, 16) != 0)
964 return next
== i
+ 16 ? i
: next
;
967 static void dump_buffer(off_t base
, unsigned char *buf
, size_t sz
)
969 size_t i
, l
, next
= 0;
973 for (i
= 0, l
= 0; i
< sz
; i
++, l
++) {
976 next
= skip_empty(buf
, i
, sz
);
977 printf("%08jx ", (intmax_t)base
+ i
);
979 printf(" %02x", buf
[i
]);
980 if (l
== 7) /* words separator */
983 fputc('\n', stdout
); /* next line */
996 static void dump_blkdev(struct fdisk_context
*cxt
, const char *name
,
997 uint64_t offset
, size_t size
)
999 int fd
= fdisk_get_devfd(cxt
);
1001 fdisk_info(cxt
, _("\n%s: offset = %"PRIu64
", size = %zu bytes."),
1002 name
, offset
, size
);
1006 if (lseek(fd
, (off_t
) offset
, SEEK_SET
) == (off_t
) -1)
1007 fdisk_warn(cxt
, _("cannot seek"));
1009 unsigned char *buf
= xmalloc(size
);
1011 if (read_all(fd
, (char *) buf
, size
) != (ssize_t
) size
)
1012 fdisk_warn(cxt
, _("cannot read"));
1014 dump_buffer(offset
, buf
, size
);
1019 void dump_firstsector(struct fdisk_context
*cxt
)
1023 dump_blkdev(cxt
, _("First sector"), 0, fdisk_get_sector_size(cxt
));
1026 void dump_disklabel(struct fdisk_context
*cxt
)
1029 const char *name
= NULL
;
1030 uint64_t offset
= 0;
1035 while (fdisk_locate_disklabel(cxt
, i
++, &name
, &offset
, &size
) == 0 && size
)
1036 dump_blkdev(cxt
, name
, offset
, size
);
1039 static fdisk_sector_t
get_dev_blocks(char *dev
)
1042 fdisk_sector_t size
;
1044 if ((fd
= open(dev
, O_RDONLY
|O_NONBLOCK
)) < 0)
1045 err(EXIT_FAILURE
, _("cannot open %s"), dev
);
1046 ret
= blkdev_get_sectors(fd
, (unsigned long long *) &size
);
1049 err(EXIT_FAILURE
, _("BLKGETSIZE ioctl failed on %s"), dev
);
1054 void follow_wipe_mode(struct fdisk_context
*cxt
)
1056 int dowipe
= wipemode
== WIPEMODE_ALWAYS
? 1 : 0;
1058 if (isatty(STDIN_FILENO
) && wipemode
== WIPEMODE_AUTO
)
1059 dowipe
= 1; /* do it in interactive mode */
1061 if (fdisk_is_ptcollision(cxt
) && wipemode
!= WIPEMODE_NEVER
)
1062 dowipe
= 1; /* always remove old PT */
1064 fdisk_enable_wipe(cxt
, dowipe
);
1067 "The device contains '%s' signature and it will be removed by a write command. "
1068 "See fdisk(8) man page and --wipe option for more details."),
1069 fdisk_get_collision(cxt
));
1072 "The device contains '%s' signature and it may remain on the device. "
1073 "It is recommended to wipe the device with wipefs(8) or "
1074 "fdisk --wipe, in order to avoid possible collisions."),
1075 fdisk_get_collision(cxt
));
1078 static void __attribute__((__noreturn__
)) usage(void)
1082 fputs(USAGE_HEADER
, out
);
1085 _(" %1$s [options] <disk> change partition table\n"
1086 " %1$s [options] -l [<disk>...] list partition table(s)\n"),
1087 program_invocation_short_name
);
1089 fputs(USAGE_SEPARATOR
, out
);
1090 fputs(_("Display or manipulate a disk partition table.\n"), out
);
1092 fputs(USAGE_OPTIONS
, out
);
1093 fputs(_(" -b, --sector-size <size> physical and logical sector size\n"), out
);
1094 fputs(_(" -B, --protect-boot don't erase bootbits when creating a new label\n"), out
);
1095 fputs(_(" -c, --compatibility[=<mode>] mode is 'dos' or 'nondos' (default)\n"), out
);
1097 _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
1099 " %s\n", USAGE_COLORS_DEFAULT
);
1100 fputs(_(" -l, --list display partitions and exit\n"), out
);
1101 fputs(_(" -x, --list-details like --list but with more details\n"), out
);
1103 fputs(_(" -n, --noauto-pt don't create default partition table on empty devices\n"), out
);
1104 fputs(_(" -o, --output <list> output columns\n"), out
);
1105 fputs(_(" -t, --type <type> recognize specified partition table type only\n"), out
);
1106 fputs(_(" -u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out
);
1107 fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out
);
1108 fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out
);
1110 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
1112 _(" -w, --wipe <mode> wipe signatures (%s, %s or %s)\n"), "auto", "always", "never");
1114 _(" -W, --wipe-partitions <mode> wipe signatures from new partitions (%s, %s or %s)\n"), "auto", "always", "never");
1116 fputs(USAGE_SEPARATOR
, out
);
1117 fputs(_(" -C, --cylinders <number> specify the number of cylinders\n"), out
);
1118 fputs(_(" -H, --heads <number> specify the number of heads\n"), out
);
1119 fputs(_(" -S, --sectors <number> specify the number of sectors per track\n"), out
);
1121 fputs(USAGE_SEPARATOR
, out
);
1122 fprintf(out
, USAGE_HELP_OPTIONS(31));
1124 list_available_columns(out
);
1126 fprintf(out
, USAGE_MAN_TAIL("fdisk(8)"));
1132 ACT_FDISK
= 0, /* default */
1138 int main(int argc
, char **argv
)
1140 int rc
, i
, c
, act
= ACT_FDISK
, noauto_pt
= 0;
1141 int colormode
= UL_COLORMODE_UNDEF
;
1142 struct fdisk_context
*cxt
;
1143 char *outarg
= NULL
;
1144 const char *devname
, *lockmode
= NULL
;
1146 OPT_BYTES
= CHAR_MAX
+ 1,
1149 static const struct option longopts
[] = {
1150 { "bytes", no_argument
, NULL
, OPT_BYTES
},
1151 { "color", optional_argument
, NULL
, 'L' },
1152 { "compatibility", optional_argument
, NULL
, 'c' },
1153 { "cylinders", required_argument
, NULL
, 'C' },
1154 { "heads", required_argument
, NULL
, 'H' },
1155 { "sectors", required_argument
, NULL
, 'S' },
1156 { "getsz", no_argument
, NULL
, 's' },
1157 { "help", no_argument
, NULL
, 'h' },
1158 { "list", no_argument
, NULL
, 'l' },
1159 { "list-details", no_argument
, NULL
, 'x' },
1160 { "lock", optional_argument
, NULL
, OPT_LOCK
},
1161 { "noauto-pt", no_argument
, NULL
, 'n' },
1162 { "sector-size", required_argument
, NULL
, 'b' },
1163 { "type", required_argument
, NULL
, 't' },
1164 { "units", optional_argument
, NULL
, 'u' },
1165 { "version", no_argument
, NULL
, 'V' },
1166 { "output", required_argument
, NULL
, 'o' },
1167 { "protect-boot", no_argument
, NULL
, 'B' },
1168 { "wipe", required_argument
, NULL
, 'w' },
1169 { "wipe-partitions",required_argument
, NULL
, 'W' },
1170 { NULL
, 0, NULL
, 0 }
1173 setlocale(LC_ALL
, "");
1174 bindtextdomain(PACKAGE
, LOCALEDIR
);
1175 textdomain(PACKAGE
);
1176 close_stdout_atexit();
1178 fdisk_init_debug(0);
1179 scols_init_debug(0);
1180 fdiskprog_init_debug();
1182 cxt
= fdisk_new_context();
1184 err(EXIT_FAILURE
, _("failed to allocate libfdisk context"));
1186 fdisk_set_ask(cxt
, ask_callback
, NULL
);
1188 while ((c
= getopt_long(argc
, argv
, "b:Bc::C:hH:lL::no:sS:t:u::vVw:W:x",
1189 longopts
, NULL
)) != -1) {
1193 size_t sz
= strtou32_or_err(optarg
,
1194 _("invalid sector size argument"));
1195 if (sz
!= 512 && sz
!= 1024 && sz
!= 2048 && sz
!= 4096)
1196 errx(EXIT_FAILURE
, _("invalid sector size argument"));
1197 fdisk_save_user_sector_size(cxt
, sz
, sz
);
1201 fdisk_enable_bootbits_protection(cxt
, 1);
1204 fdisk_save_user_geometry(cxt
,
1205 strtou32_or_err(optarg
,
1206 _("invalid cylinders argument")),
1211 /* this setting is independent on the current
1212 * actively used label
1214 char *p
= *optarg
== '=' ? optarg
+ 1 : optarg
;
1215 struct fdisk_label
*lb
= fdisk_get_label(cxt
, "dos");
1218 err(EXIT_FAILURE
, _("not found DOS label driver"));
1219 if (strcmp(p
, "dos") == 0)
1220 fdisk_dos_enable_compatible(lb
, TRUE
);
1221 else if (strcmp(p
, "nondos") == 0)
1222 fdisk_dos_enable_compatible(lb
, FALSE
);
1224 errx(EXIT_FAILURE
, _("unknown compatibility mode '%s'"), p
);
1226 /* use default if no optarg specified */
1229 fdisk_save_user_geometry(cxt
, 0,
1230 strtou32_or_err(optarg
,
1231 _("invalid heads argument")),
1235 fdisk_save_user_geometry(cxt
, 0, 0,
1236 strtou32_or_err(optarg
,
1237 _("invalid sectors argument")));
1243 act
= ACT_LIST_DETAILS
;
1246 colormode
= UL_COLORMODE_AUTO
;
1248 colormode
= colormode_or_err(optarg
,
1249 _("unsupported color mode"));
1262 struct fdisk_label
*lb
= NULL
;
1264 while (fdisk_next_label(cxt
, &lb
) == 0)
1265 fdisk_label_set_disabled(lb
, 1);
1267 lb
= fdisk_get_label(cxt
, optarg
);
1269 errx(EXIT_FAILURE
, _("unsupported disklabel: %s"), optarg
);
1270 fdisk_label_set_disabled(lb
, 0);
1274 if (optarg
&& *optarg
== '=')
1276 if (fdisk_set_unit(cxt
, optarg
) != 0)
1277 errx(EXIT_FAILURE
, _("unsupported unit"));
1279 case 'V': /* preferred for util-linux */
1280 case 'v': /* for backward compatibility only */
1281 print_version(EXIT_SUCCESS
);
1283 wipemode
= wipemode_from_string(optarg
);
1285 errx(EXIT_FAILURE
, _("unsupported wipe mode"));
1288 pwipemode
= wipemode_from_string(optarg
);
1290 errx(EXIT_FAILURE
, _("unsupported wipe mode"));
1295 fdisk_set_size_unit(cxt
, FDISK_SIZEUNIT_BYTES
);
1306 errtryhelp(EXIT_FAILURE
);
1310 if (argc
-optind
!= 1 && fdisk_has_user_device_properties(cxt
))
1311 warnx(_("The device properties (sector size and geometry) should"
1312 " be used with one specified device only."));
1314 colors_init(colormode
, "fdisk");
1315 is_interactive
= isatty(STDIN_FILENO
);
1319 case ACT_LIST_DETAILS
:
1320 fdisk_enable_listonly(cxt
, 1);
1322 if (act
== ACT_LIST_DETAILS
)
1323 fdisk_enable_details(cxt
, 1);
1325 init_fields(cxt
, outarg
, NULL
);
1327 if (argc
> optind
) {
1330 for (rc
= 0, k
= optind
; k
< argc
; k
++)
1331 rc
+= print_device_pt(cxt
, argv
[k
], 1, 0, k
!= optind
);
1334 return EXIT_FAILURE
;
1336 print_all_devices_pt(cxt
, 0);
1341 if (argc
- optind
<= 0) {
1342 warnx(_("bad usage"));
1343 errtryhelp(EXIT_FAILURE
);
1345 for (i
= optind
; i
< argc
; i
++) {
1346 uintmax_t blks
= get_dev_blocks(argv
[i
]);
1348 if (argc
- optind
== 1)
1349 printf("%ju\n", blks
);
1351 printf("%s: %ju\n", argv
[i
], blks
);
1356 if (argc
-optind
!= 1) {
1357 warnx(_("bad usage"));
1358 errtryhelp(EXIT_FAILURE
);
1361 /* Here starts interactive mode, use fdisk_{warn,info,..} functions */
1362 color_scheme_enable("welcome", UL_COLOR_GREEN
);
1363 fdisk_info(cxt
, _("Welcome to fdisk (%s)."), PACKAGE_STRING
);
1365 fdisk_info(cxt
, _("Changes will remain in memory only, until you decide to write them.\n"
1366 "Be careful before using the write command.\n"));
1368 devname
= argv
[optind
];
1369 rc
= fdisk_assign_device(cxt
, devname
, 0);
1370 if (rc
== -EACCES
) {
1371 rc
= fdisk_assign_device(cxt
, devname
, 1);
1373 fdisk_warnx(cxt
, _("Device is open in read-only mode."));
1376 err(EXIT_FAILURE
, _("cannot open %s"), devname
);
1378 if (fdisk_device_is_used(cxt
))
1380 "This disk is currently in use - repartitioning is probably a bad idea.\n"
1381 "It's recommended to umount all file systems, and swapoff all swap\n"
1382 "partitions on this disk.\n"));
1386 if (!fdisk_is_readonly(cxt
)
1387 && blkdev_lock(fdisk_get_devfd(cxt
), devname
, lockmode
) != 0) {
1388 fdisk_deassign_device(cxt
, 1);
1389 fdisk_unref_context(cxt
);
1390 return EXIT_FAILURE
;
1393 if (fdisk_get_collision(cxt
))
1394 follow_wipe_mode(cxt
);
1396 if (!fdisk_has_label(cxt
)) {
1397 fdisk_info(cxt
, _("Device does not contain a recognized partition table."));
1399 fdisk_create_disklabel(cxt
, NULL
);
1401 } else if (fdisk_is_label(cxt
, GPT
) && fdisk_gpt_is_hybrid(cxt
))
1403 "A hybrid GPT was detected. You have to sync "
1404 "the hybrid MBR manually (expert command 'M')."));
1406 init_fields(cxt
, outarg
, NULL
); /* -o <columns> */
1408 if (!fdisk_is_readonly(cxt
)) {
1409 fdisk_get_partitions(cxt
, &original_layout
);
1410 device_is_used
= fdisk_device_is_used(cxt
);
1414 process_fdisk_menu(&cxt
);
1417 fdisk_unref_context(cxt
);
1418 return EXIT_SUCCESS
;