]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/fdisk.c
fdisk: add wrap_fgets() for getting user input
[thirdparty/util-linux.git] / disk-utils / fdisk.c
CommitLineData
3f0ac587 1/*
6dbe3af9 2 * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
38b36353 3 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
6dbe3af9 4 *
3f0ac587
KZ
5 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
6 *
6dbe3af9
KZ
7 * This program is free software. You can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation: either version 1 or
10 * (at your option) any later version.
6dbe3af9 11 */
6dbe3af9
KZ
12#include <unistd.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <fcntl.h>
17#include <ctype.h>
6dbe3af9 18#include <errno.h>
2b6fc908 19#include <getopt.h>
2b6fc908 20#include <sys/stat.h>
f26873dc 21#include <sys/time.h>
bb6aacfe 22#include <time.h>
ee5355e0 23#include <limits.h>
d44115f3 24#include <libsmartcols.h>
740c36f6 25#ifdef HAVE_LIBREADLINE
57ebfde2 26# define _FUNCTION_DEF
740c36f6
KZ
27# include <readline/readline.h>
28#endif
6dbe3af9 29
3f0ac587 30#include "c.h"
52b38677 31#include "xalloc.h"
e916600f 32#include "all-io.h"
22853e4a 33#include "nls.h"
e66ac5d3 34#include "rpmatch.h"
810f986b 35#include "blkdev.h"
b8d22034 36#include "mbsalign.h"
6bec8710
KZ
37#include "pathnames.h"
38#include "canonicalize.h"
20aa2570 39#include "strutils.h"
b2d28533 40#include "closestream.h"
8d2f4498 41#include "pager.h"
5c36a0eb 42
fdb006e8
KZ
43#include "fdisk.h"
44
b4bfbadd 45#include "pt-sun.h" /* to toggle flags */
5c36a0eb 46
48d7b13a 47#ifdef HAVE_LINUX_COMPILER_H
3f0ac587 48# include <linux/compiler.h>
48d7b13a
KZ
49#endif
50#ifdef HAVE_LINUX_BLKPG_H
3f0ac587 51# include <linux/blkpg.h>
7eda085c 52#endif
6dbe3af9 53
ba465623 54int pwipemode = WIPEMODE_AUTO;
fadd8e08
KZ
55int device_is_used;
56struct fdisk_table *original_layout;
57
5635d195 58static int wipemode = WIPEMODE_AUTO;
ba465623 59
e6d0c4c1
KZ
60/*
61 * fdisk debug stuff (see fdisk.h and include/debug.h)
62 */
63UL_DEBUG_DEFINE_MASK(fdisk);
819d9a29 64UL_DEBUG_DEFINE_MASKNAMES(fdisk) = UL_DEBUG_EMPTY_MASKNAMES;
d6b3ba41 65
e6d0c4c1
KZ
66static void fdiskprog_init_debug(void)
67{
68 __UL_INIT_DEBUG(fdisk, FDISKPROG_DEBUG_, 0, FDISK_DEBUG);
69}
d6b3ba41 70
740c36f6
KZ
71#ifdef HAVE_LIBREADLINE
72static char *rl_fgets(char *s, int n, FILE *stream, const char *prompt)
73{
74 char *p;
75
76 rl_outstream = stream;
77 p = readline(prompt);
78 if (!p)
79 return NULL;
80
84ab260a
KZ
81 strncpy(s, p, n);
82 s[n - 1] = '\0';
740c36f6
KZ
83 free(p);
84 return s;
85}
86#endif
87
55cf3716
VD
88static char *wrap_fgets(char *s, int n, FILE *stream, const char *prompt)
89{
90#ifdef HAVE_LIBREADLINE
91 if (isatty(STDIN_FILENO)) {
92 return rl_fgets(s, n, stream, prompt);
93 }
94 else
95#endif
96 {
97 fputs(prompt, stream);
98 fflush(stream);
99 return fgets(s, n, stdin);
100 }
101}
102
d6b3ba41
KZ
103int get_user_reply(struct fdisk_context *cxt, const char *prompt,
104 char *buf, size_t bufsz)
105{
106 char *p;
107 size_t sz;
108
109 do {
55cf3716
VD
110 if (!wrap_fgets(buf, bufsz, stdout, prompt)) {
111 if (fdisk_label_is_changed(fdisk_get_label(cxt, NULL))) {
112 if (wrap_fgets(buf, bufsz, stderr,
113 _("\nDo you really want to quit? "))
114 && !rpmatch(buf))
115 continue;
116 }
117 fdisk_unref_context(cxt);
118 exit(EXIT_FAILURE);
119 } else
120 break;
d6b3ba41
KZ
121 } while (1);
122
123 for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */
124
125 if (p > buf)
126 memmove(buf, p, p - buf); /* remove blank space */
127 sz = strlen(buf);
128 if (sz && *(buf + sz - 1) == '\n')
129 *(buf + sz - 1) = '\0';
130
88141067 131 DBG(ASK, ul_debug("user's reply: >>>%s<<<", buf));
d6b3ba41
KZ
132 return 0;
133}
134
135static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask,
136 char *buf, size_t bufsz)
137
138{
139 const char *q = fdisk_ask_get_query(ask);
140 int dft = fdisk_ask_menu_get_default(ask);
141
142 if (q) {
143 fputs(q, stdout); /* print header */
144 fputc('\n', stdout);
145 }
146
147 do {
148 char prompt[128];
68ddb136 149 int key, c, rc;
d6b3ba41
KZ
150 const char *name, *desc;
151 size_t i = 0;
152
153 /* print menu items */
154 while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0)
155 fprintf(stdout, " %c %s (%s)\n", key, name, desc);
156
157 /* ask for key */
158 snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft);
68ddb136
KZ
159 rc = get_user_reply(cxt, prompt, buf, bufsz);
160 if (rc)
161 return rc;
d6b3ba41
KZ
162 if (!*buf) {
163 fdisk_info(cxt, _("Using default response %c."), dft);
164 c = dft;
165 } else
166 c = tolower(buf[0]);
167
168 /* check result */
169 i = 0;
170 while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) {
171 if (c == key) {
172 fdisk_ask_menu_set_result(ask, c);
173 return 0; /* success */
174 }
175 }
176 fdisk_warnx(cxt, _("Value out of range."));
177 } while (1);
178
179 return -EINVAL;
180}
181
182
183#define tochar(num) ((int) ('a' + num - 1))
184static int ask_number(struct fdisk_context *cxt,
185 struct fdisk_ask *ask,
186 char *buf, size_t bufsz)
187{
188 char prompt[128] = { '\0' };
189 const char *q = fdisk_ask_get_query(ask);
190 const char *range = fdisk_ask_number_get_range(ask);
191
192 uint64_t dflt = fdisk_ask_number_get_default(ask),
193 low = fdisk_ask_number_get_low(ask),
194 high = fdisk_ask_number_get_high(ask);
195 int inchar = fdisk_ask_number_inchars(ask);
196
197 assert(q);
198
88141067 199 DBG(ASK, ul_debug("asking for number "
fdbd7bb9 200 "['%s', <%"PRIu64",%"PRIu64">, default=%"PRIu64", range: %s]",
d6b3ba41
KZ
201 q, low, high, dflt, range));
202
203 if (range && dflt >= low && dflt <= high) {
204 if (inchar)
205 snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "),
206 q, range, tochar(dflt));
207 else
fdbd7bb9 208 snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
d6b3ba41
KZ
209 q, range, dflt);
210
211 } else if (dflt >= low && dflt <= high) {
212 if (inchar)
213 snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "),
214 q, tochar(low), tochar(high), tochar(dflt));
215 else
fdbd7bb9
RM
216 snprintf(prompt, sizeof(prompt),
217 _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
d6b3ba41
KZ
218 q, low, high, dflt);
219 } else if (inchar)
220 snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "),
221 q, tochar(low), tochar(high));
222 else
fdbd7bb9 223 snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
d6b3ba41
KZ
224 q, low, high);
225
226 do {
227 int rc = get_user_reply(cxt, prompt, buf, bufsz);
228 uint64_t num;
229
230 if (rc)
231 return rc;
232 if (!*buf && dflt >= low && dflt <= high)
233 return fdisk_ask_number_set_result(ask, dflt);
234
235 if (isdigit_string(buf)) {
236 char *end;
237
238 errno = 0;
239 num = strtoumax(buf, &end, 10);
240 if (errno || buf == end || (end && *end))
241 continue;
242 } else if (inchar && isalpha(*buf)) {
243 num = tolower(*buf) - 'a' + 1;
244 } else
245 rc = -EINVAL;
246
247 if (rc == 0 && num >= low && num <= high)
248 return fdisk_ask_number_set_result(ask, num);
249
250 fdisk_warnx(cxt, _("Value out of range."));
251 } while (1);
252
253 return -1;
254}
255
256static int ask_offset(struct fdisk_context *cxt,
257 struct fdisk_ask *ask,
258 char *buf, size_t bufsz)
259{
260 char prompt[128] = { '\0' };
261 const char *q = fdisk_ask_get_query(ask);
262 const char *range = fdisk_ask_number_get_range(ask);
263
264 uint64_t dflt = fdisk_ask_number_get_default(ask),
265 low = fdisk_ask_number_get_low(ask),
266 high = fdisk_ask_number_get_high(ask),
267 base = fdisk_ask_number_get_base(ask);
268
269 assert(q);
270
fdbd7bb9 271 DBG(ASK, ul_debug("asking for offset ['%s', <%"PRIu64",%"PRIu64">, base=%"PRIu64", default=%"PRIu64", range: %s]",
d6b3ba41
KZ
272 q, low, high, base, dflt, range));
273
274 if (range && dflt >= low && dflt <= high)
fdbd7bb9
RM
275 snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "),
276 q, range, dflt);
d6b3ba41 277 else if (dflt >= low && dflt <= high)
fdbd7bb9
RM
278 snprintf(prompt, sizeof(prompt),
279 _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "),
280 q, low, high, dflt);
d6b3ba41 281 else
fdbd7bb9
RM
282 snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "),
283 q, low, high);
d6b3ba41
KZ
284
285 do {
262c94c2 286 uintmax_t num = 0;
d6b3ba41
KZ
287 char sig = 0, *p;
288 int pwr = 0;
289
290 int rc = get_user_reply(cxt, prompt, buf, bufsz);
291 if (rc)
292 return rc;
293 if (!*buf && dflt >= low && dflt <= high)
294 return fdisk_ask_number_set_result(ask, dflt);
295
296 p = buf;
297 if (*p == '+' || *p == '-') {
298 sig = *buf;
299 p++;
300 }
301
302 rc = parse_size(p, &num, &pwr);
303 if (rc)
304 continue;
88141067 305 DBG(ASK, ul_debug("parsed size: %ju", num));
d6b3ba41
KZ
306 if (sig && pwr) {
307 /* +{size}{K,M,...} specified, the "num" is in bytes */
308 uint64_t unit = fdisk_ask_number_get_unit(ask);
309 num += unit/2; /* round */
310 num /= unit;
311 }
312 if (sig == '+')
313 num += base;
314 else if (sig == '-')
315 num = base - num;
316
88141067 317 DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]",
d6b3ba41
KZ
318 num, sig, pwr,
319 sig ? "relative" : "absolute"));
320 if (num >= low && num <= high) {
8c73e509 321 if (sig && pwr)
d6b3ba41 322 fdisk_ask_number_set_relative(ask, 1);
262c94c2 323 return fdisk_ask_number_set_result(ask, (uint64_t)num);
d6b3ba41
KZ
324 }
325 fdisk_warnx(cxt, _("Value out of range."));
326 } while (1);
327
328 return -1;
329}
330
331static unsigned int info_count;
332
412791a9 333static void fputs_info(struct fdisk_ask *ask, FILE *out)
d6b3ba41
KZ
334{
335 const char *msg;
d6b3ba41
KZ
336 assert(ask);
337
338 msg = fdisk_ask_print_get_mesg(ask);
d6b3ba41
KZ
339 if (!msg)
340 return;
341 if (info_count == 1)
342 fputc('\n', out);
412791a9
KZ
343
344 fputs(msg, out);
d6b3ba41
KZ
345 fputc('\n', out);
346}
347
348int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
349 void *data __attribute__((__unused__)))
350{
351 int rc = 0;
352 char buf[BUFSIZ];
353
354 assert(cxt);
355 assert(ask);
356
357 if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO)
358 info_count = 0;
359
360 switch(fdisk_ask_get_type(ask)) {
361 case FDISK_ASKTYPE_MENU:
362 return ask_menu(cxt, ask, buf, sizeof(buf));
363 case FDISK_ASKTYPE_NUMBER:
364 return ask_number(cxt, ask, buf, sizeof(buf));
365 case FDISK_ASKTYPE_OFFSET:
366 return ask_offset(cxt, ask, buf, sizeof(buf));
367 case FDISK_ASKTYPE_INFO:
152788aa
KZ
368 if (!fdisk_is_listonly(cxt))
369 info_count++;
412791a9 370 fputs_info(ask, stdout);
d6b3ba41
KZ
371 break;
372 case FDISK_ASKTYPE_WARNX:
95aae4fc 373 fflush(stdout);
496c979a 374 color_scheme_fenable("warn", UL_COLOR_RED, stderr);
d6b3ba41
KZ
375 fputs(fdisk_ask_print_get_mesg(ask), stderr);
376 color_fdisable(stderr);
377 fputc('\n', stderr);
378 break;
379 case FDISK_ASKTYPE_WARN:
95aae4fc 380 fflush(stdout);
496c979a 381 color_scheme_fenable("warn", UL_COLOR_RED, stderr);
d6b3ba41
KZ
382 fputs(fdisk_ask_print_get_mesg(ask), stderr);
383 errno = fdisk_ask_print_get_errno(ask);
384 fprintf(stderr, ": %m\n");
385 color_fdisable(stderr);
386 break;
387 case FDISK_ASKTYPE_YESNO:
388 fputc('\n', stdout);
2d129032
KZ
389 do {
390 int x;
391 fputs(fdisk_ask_get_query(ask), stdout);
392 rc = get_user_reply(cxt, _(" [Y]es/[N]o: "), buf, sizeof(buf));
393 if (rc)
394 break;
395 x = rpmatch(buf);
cd2a6f1c 396 if (x == RPMATCH_YES || x == RPMATCH_NO) {
2d129032
KZ
397 fdisk_ask_yesno_set_result(ask, x);
398 break;
399 }
400 } while(1);
88141067 401 DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc));
d6b3ba41
KZ
402 break;
403 case FDISK_ASKTYPE_STRING:
404 {
405 char prmt[BUFSIZ];
406 snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask));
407 fputc('\n', stdout);
408 rc = get_user_reply(cxt, prmt, buf, sizeof(buf));
409 if (rc == 0)
410 fdisk_ask_string_set_result(ask, xstrdup(buf));
88141067 411 DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc));
d6b3ba41
KZ
412 break;
413 }
414 default:
415 warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
416 return -EINVAL;
417 }
418 return rc;
419}
420
4bd02cdf 421static struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt)
d6b3ba41
KZ
422{
423 const char *q;
a745611d
KZ
424 struct fdisk_label *lb;
425
426 assert(cxt);
427 lb = fdisk_get_label(cxt, NULL);
d6b3ba41 428
a745611d 429 if (!lb)
d6b3ba41
KZ
430 return NULL;
431
a745611d 432 q = fdisk_label_has_code_parttypes(lb) ?
84111dba
KZ
433 _("Hex code (type L to list all codes): ") :
434 _("Partition type (type L to list all types): ");
d6b3ba41
KZ
435 do {
436 char buf[256];
437 int rc = get_user_reply(cxt, q, buf, sizeof(buf));
438
439 if (rc)
440 break;
441
442 if (buf[1] == '\0' && toupper(*buf) == 'L')
443 list_partition_types(cxt);
444 else if (*buf)
a745611d 445 return fdisk_label_parse_parttype(lb, buf);
d6b3ba41
KZ
446 } while (1);
447
448 return NULL;
449}
450
8d2f4498 451
7b575fcc 452void list_partition_types(struct fdisk_context *cxt)
6dbe3af9 453{
76f17cf2 454 size_t ntypes = 0;
f45f5c83 455 struct fdisk_label *lb;
6dbe3af9 456
a745611d
KZ
457 assert(cxt);
458 lb = fdisk_get_label(cxt, NULL);
459 if (!lb)
460 return;
461 ntypes = fdisk_label_get_nparttypes(lb);
462 if (!ntypes)
7b575fcc 463 return;
2b6fc908 464
a745611d 465 if (fdisk_label_has_code_parttypes(lb)) {
7b575fcc
KZ
466 /*
467 * Prints in 4 columns in format <hex> <name>
468 */
76f17cf2
KZ
469 size_t last[4], done = 0, next = 0, size;
470 int i;
b8d22034 471
76f17cf2 472 size = ntypes;
7b575fcc
KZ
473
474 for (i = 3; i >= 0; i--)
475 last[3 - i] = done += (size + i - done) / (i + 1);
476 i = done = 0;
477
478 do {
479 #define NAME_WIDTH 15
480 char name[NAME_WIDTH * MB_LEN_MAX];
481 size_t width = NAME_WIDTH;
a745611d 482 const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, next);
7b575fcc
KZ
483 size_t ret;
484
e6d0c4c1 485 if (fdisk_parttype_get_name(t)) {
a745611d
KZ
486 printf("%c%2x ", i ? ' ' : '\n',
487 fdisk_parttype_get_code(t));
488 ret = mbsalign(_(fdisk_parttype_get_name(t)),
489 name, sizeof(name),
490 &width, MBS_ALIGN_LEFT, 0);
7b575fcc 491
76f17cf2 492 if (ret == (size_t)-1 || ret >= sizeof(name))
a745611d
KZ
493 printf("%-15.15s",
494 _(fdisk_parttype_get_name(t)));
76f17cf2
KZ
495 else
496 fputs(name, stdout);
497 }
7b575fcc
KZ
498
499 next = last[i++] + done;
500 if (i > 3 || next >= last[i]) {
501 i = 0;
502 next = ++done;
503 }
504 } while (done < last[0]);
505
506 } else {
507 /*
508 * Prints 1 column in format <idx> <name> <typestr>
509 */
76f17cf2 510 size_t i;
7b575fcc 511
8d2f4498
KZ
512 pager_open();
513
a745611d
KZ
514 for (i = 0; i < ntypes; i++) {
515 const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, i);
516 printf("%3zu %-30s %s\n", i + 1,
517 fdisk_parttype_get_name(t),
518 fdisk_parttype_get_string(t));
76f17cf2 519 }
8d2f4498
KZ
520
521 pager_close();
7b575fcc 522 }
6dbe3af9
KZ
523 putchar('\n');
524}
525
f02fecd1 526void toggle_dos_compatibility_flag(struct fdisk_context *cxt)
852ce62b 527{
6a632136 528 struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
852ce62b
KZ
529 int flag;
530
531 if (!lb)
532 return;
533
534 flag = !fdisk_dos_is_compatible(lb);
09f0c3d9
KZ
535 fdisk_info(cxt, flag ?
536 _("DOS Compatibility flag is set (DEPRECATED!)") :
537 _("DOS Compatibility flag is not set"));
edd7b958 538
852ce62b
KZ
539 fdisk_dos_enable_compatible(lb, flag);
540
aa36c2cf 541 if (fdisk_is_label(cxt, DOS))
09f0c3d9 542 fdisk_reset_alignment(cxt); /* reset the current label */
6dbe3af9
KZ
543}
544
27ddd4f1 545void change_partition_type(struct fdisk_context *cxt)
e53ced85 546{
641a75ca 547 size_t i;
85151521
KZ
548 struct fdisk_parttype *t = NULL;
549 struct fdisk_partition *pa = NULL;
550 const char *old = NULL;
6dbe3af9 551
e3661531 552 assert(cxt);
e3661531 553
641a75ca 554 if (fdisk_ask_partnum(cxt, &i, FALSE))
24f4bbff 555 return;
2b6fc908 556
85151521
KZ
557 if (fdisk_get_partition(cxt, i, &pa)) {
558 fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
559 return;
560 }
561
562 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
e6d0c4c1 563 old = t ? fdisk_parttype_get_name(t) : _("Unknown");
85151521
KZ
564
565 do {
566 t = ask_partition_type(cxt);
567 } while (!t);
568
85151521 569 if (fdisk_set_partition_type(cxt, i, t) == 0)
0477369a 570 fdisk_info(cxt,
85151521 571 _("Changed type of partition '%s' to '%s'."),
a745611d 572 old, t ? fdisk_parttype_get_name(t) : _("Unknown"));
85151521
KZ
573 else
574 fdisk_info(cxt,
575 _("Type of partition %zu is unchanged: %s."),
576 i + 1, old);
577
d6cfa8b3 578 fdisk_unref_partition(pa);
4bd02cdf 579 fdisk_unref_parttype(t);
6dbe3af9
KZ
580}
581
3e3b51b3
JLB
582int print_partition_info(struct fdisk_context *cxt)
583{
584 struct fdisk_partition *pa = NULL;
6360bcca 585 int rc = 0;
3e3b51b3
JLB
586 size_t i, nfields;
587 int *fields = NULL;
588 struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
589
590 if ((rc = fdisk_ask_partnum(cxt, &i, FALSE)))
591 return rc;
592
593 if ((rc = fdisk_get_partition(cxt, i, &pa))) {
594 fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
595 return rc;
596 }
597
6360bcca 598 if ((rc = fdisk_label_get_fields_ids_all(lb, cxt, &fields, &nfields)))
3e3b51b3 599 goto clean_data;
3e3b51b3
JLB
600
601 for (i = 0; i < nfields; ++i) {
602 int id = fields[i];
603 char *data = NULL;
604 const struct fdisk_field *fd = fdisk_label_get_field(lb, id);
605
606 if (!fd)
607 continue;
608
609 rc = fdisk_partition_to_string(pa, cxt, id, &data);
610 if (rc < 0)
611 goto clean_data;
6360bcca
KZ
612 if (!data || !*data)
613 continue;
3e3b51b3
JLB
614 fdisk_info(cxt, _("%15s: %s"), fdisk_field_get_name(fd), data);
615 free(data);
616 }
617
618clean_data:
619 fdisk_unref_partition(pa);
620 free(fields);
621 return rc;
622}
623
e916600f
KZ
624static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz)
625{
626 size_t next;
627 const unsigned char *p0 = buf + i;
628
629 for (next = i + 16; next < sz; next += 16) {
630 if (memcmp(p0, buf + next, 16) != 0)
631 break;
632 }
633
634 return next == i + 16 ? i : next;
635}
6dbe3af9 636
e916600f 637static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all)
09f0c3d9 638{
e916600f
KZ
639 size_t i, l, next = 0;
640
641 if (!buf)
642 return;
643 for (i = 0, l = 0; i < sz; i++, l++) {
644 if (l == 0) {
645 if (all == 0 && !next)
646 next = skip_empty(buf, i, sz);
fdbd7bb9 647 printf("%08jx ", (intmax_t)base + i);
e916600f
KZ
648 }
649 printf(" %02x", buf[i]);
650 if (l == 7) /* words separator */
651 fputs(" ", stdout);
652 else if (l == 15) {
653 fputc('\n', stdout); /* next line */
6dbe3af9 654 l = -1;
e916600f
KZ
655 if (next > i) {
656 printf("*\n");
657 i = next - 1;
658 }
659 next = 0;
6dbe3af9
KZ
660 }
661 }
662 if (l > 0)
663 printf("\n");
6dbe3af9
KZ
664}
665
e916600f 666static void dump_blkdev(struct fdisk_context *cxt, const char *name,
9bbcf43f 667 uint64_t offset, size_t size, int all)
e53ced85 668{
8eccde20
KZ
669 int fd = fdisk_get_devfd(cxt);
670
fdbd7bb9 671 fdisk_info(cxt, _("\n%s: offset = %"PRIu64", size = %zu bytes."),
e916600f
KZ
672 name, offset, size);
673
8eccde20
KZ
674 assert(fd >= 0);
675
9bbcf43f 676 if (lseek(fd, (off_t) offset, SEEK_SET) == (off_t) -1)
e916600f 677 fdisk_warn(cxt, _("cannot seek"));
5afed187
KZ
678 else {
679 unsigned char *buf = xmalloc(size);
680
8eccde20 681 if (read_all(fd, (char *) buf, size) != (ssize_t) size)
5afed187
KZ
682 fdisk_warn(cxt, _("cannot read"));
683 else
684 dump_buffer(offset, buf, size, all);
685 free(buf);
686 }
e916600f
KZ
687}
688
689void dump_firstsector(struct fdisk_context *cxt)
690{
691 int all = !isatty(STDOUT_FILENO);
692
e3661531 693 assert(cxt);
6dbe3af9 694
8eccde20 695 dump_blkdev(cxt, _("First sector"), 0, fdisk_get_sector_size(cxt), all);
e916600f
KZ
696}
697
698void dump_disklabel(struct fdisk_context *cxt)
699{
700 int all = !isatty(STDOUT_FILENO);
701 int i = 0;
702 const char *name = NULL;
9bbcf43f 703 uint64_t offset = 0;
e916600f
KZ
704 size_t size = 0;
705
706 assert(cxt);
e3661531 707
e916600f
KZ
708 while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size)
709 dump_blkdev(cxt, name, offset, size, all);
6dbe3af9
KZ
710}
711
0073a4cf 712static fdisk_sector_t get_dev_blocks(char *dev)
6da365de 713{
bbe67996 714 int fd, ret;
0073a4cf 715 fdisk_sector_t size;
6da365de
DB
716
717 if ((fd = open(dev, O_RDONLY)) < 0)
289dcc90 718 err(EXIT_FAILURE, _("cannot open %s"), dev);
0073a4cf 719 ret = blkdev_get_sectors(fd, (unsigned long long *) &size);
6da365de 720 close(fd);
bbe67996
SK
721 if (ret < 0)
722 err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
6da365de
DB
723 return size/2;
724}
725
5635d195
KZ
726
727void follow_wipe_mode(struct fdisk_context *cxt)
728{
729 int dowipe = wipemode == WIPEMODE_ALWAYS ? 1 : 0;
730
731 if (isatty(STDIN_FILENO) && wipemode == WIPEMODE_AUTO)
732 dowipe = 1; /* do it in interactive mode */
733
734 if (fdisk_is_ptcollision(cxt) && wipemode != WIPEMODE_NEVER)
735 dowipe = 1; /* always remove old PT */
736
737 fdisk_enable_wipe(cxt, dowipe);
738 if (dowipe)
739 fdisk_warnx(cxt, _(
740 "The old %s signature will be removed by a write command."),
741 fdisk_get_collision(cxt));
742 else
743 fdisk_warnx(cxt, _(
744 "The old %s signature may remain on the device. "
745 "It is recommended to wipe the device with wipefs(8) or "
746 "fdisk --wipe, in order to avoid possible collisions."),
747 fdisk_get_collision(cxt));
748}
749
6e1eda6f 750static void __attribute__((__noreturn__)) usage(void)
e3a4aaa7 751{
6e1eda6f
RM
752 FILE *out = stdout;
753
e3a4aaa7
KZ
754 fputs(USAGE_HEADER, out);
755
756 fprintf(out,
757 _(" %1$s [options] <disk> change partition table\n"
758 " %1$s [options] -l [<disk>] list partition table(s)\n"),
759 program_invocation_short_name);
760
451dbcfa
BS
761 fputs(USAGE_SEPARATOR, out);
762 fputs(_("Display or manipulate a disk partition table.\n"), out);
763
e3a4aaa7
KZ
764 fputs(USAGE_OPTIONS, out);
765 fputs(_(" -b, --sector-size <size> physical and logical sector size\n"), out);
718b6f0c 766 fputs(_(" -B, --protect-boot don't erase bootbits when creating a new label\n"), out);
e3a4aaa7
KZ
767 fputs(_(" -c, --compatibility[=<mode>] mode is 'dos' or 'nondos' (default)\n"), out);
768 fputs(_(" -L, --color[=<when>] colorize output (auto, always or never)\n"), out);
5d51dc2a
KZ
769 fprintf(out,
770 " %s\n", USAGE_COLORS_DEFAULT);
9e930041 771 fputs(_(" -l, --list display partitions and exit\n"), out);
2d7dffeb 772 fputs(_(" -o, --output <list> output columns\n"), out);
e3a4aaa7
KZ
773 fputs(_(" -t, --type <type> recognize specified partition table type only\n"), out);
774 fputs(_(" -u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out);
775 fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out);
354f8cc8 776 fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out);
cb9a4b00 777 fputs(_(" -w, --wipe <mode> wipe signatures (auto, always or never)\n"), out);
ba465623 778 fputs(_(" -W, --wipe-partitions <mode> wipe signatures from new partitions (auto, always or never)\n"), out);
e3a4aaa7
KZ
779
780 fputs(USAGE_SEPARATOR, out);
4b4e391a
KZ
781 fputs(_(" -C, --cylinders <number> specify the number of cylinders\n"), out);
782 fputs(_(" -H, --heads <number> specify the number of heads\n"), out);
783 fputs(_(" -S, --sectors <number> specify the number of sectors per track\n"), out);
e3a4aaa7
KZ
784
785 fputs(USAGE_SEPARATOR, out);
f45f3ec3 786 printf(USAGE_HELP_OPTIONS(31));
e3a4aaa7 787
fff8ad58
KZ
788 list_available_columns(out);
789
f45f3ec3 790 printf(USAGE_MAN_TAIL("fdisk(8)"));
6e1eda6f 791 exit(EXIT_SUCCESS);
e3a4aaa7
KZ
792}
793
794
5b72b3dd
KZ
795enum {
796 ACT_FDISK = 0, /* default */
797 ACT_LIST,
798 ACT_SHOWSIZE
799};
800
e1144767
DB
801int main(int argc, char **argv)
802{
e146ae4e 803 int rc, i, c, act = ACT_FDISK;
d0c9ddc3 804 int colormode = UL_COLORMODE_UNDEF;
4e0e8253 805 struct fdisk_context *cxt;
fff8ad58 806 char *outarg = NULL;
354f8cc8
KZ
807 enum {
808 OPT_BYTES = CHAR_MAX + 1
809 };
e3a4aaa7 810 static const struct option longopts[] = {
354f8cc8 811 { "bytes", no_argument, NULL, OPT_BYTES },
e3a4aaa7
KZ
812 { "color", optional_argument, NULL, 'L' },
813 { "compatibility", optional_argument, NULL, 'c' },
4b4e391a
KZ
814 { "cylinders", required_argument, NULL, 'C' },
815 { "heads", required_argument, NULL, 'H' },
816 { "sectors", required_argument, NULL, 'S' },
e3a4aaa7
KZ
817 { "getsz", no_argument, NULL, 's' },
818 { "help", no_argument, NULL, 'h' },
819 { "list", no_argument, NULL, 'l' },
820 { "sector-size", required_argument, NULL, 'b' },
821 { "type", required_argument, NULL, 't' },
822 { "units", optional_argument, NULL, 'u' },
823 { "version", no_argument, NULL, 'V' },
2d7dffeb 824 { "output", no_argument, NULL, 'o' },
aeb9a30b 825 { "protect-boot", no_argument, NULL, 'B' },
cb9a4b00 826 { "wipe", required_argument, NULL, 'w' },
ba465623 827 { "wipe-partitions",required_argument, NULL, 'W' },
e3a4aaa7
KZ
828 { NULL, 0, NULL, 0 }
829 };
830
7eda085c
KZ
831 setlocale(LC_ALL, "");
832 bindtextdomain(PACKAGE, LOCALEDIR);
833 textdomain(PACKAGE);
b2d28533 834 atexit(close_stdout);
2b6fc908 835
4e0e8253 836 fdisk_init_debug(0);
053939a4 837 scols_init_debug(0);
e6d0c4c1
KZ
838 fdiskprog_init_debug();
839
4e0e8253
KZ
840 cxt = fdisk_new_context();
841 if (!cxt)
842 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
843
6a632136 844 fdisk_set_ask(cxt, ask_callback, NULL);
416c43a9 845
ba465623 846 while ((c = getopt_long(argc, argv, "b:Bc::C:hH:lL::o:sS:t:u::vVw:W:",
e3a4aaa7 847 longopts, NULL)) != -1) {
2b6fc908
KZ
848 switch (c) {
849 case 'b':
6bcd192c
KZ
850 {
851 size_t sz = strtou32_or_err(optarg,
852 _("invalid sector size argument"));
853 if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096)
6e1eda6f 854 errx(EXIT_FAILURE, _("invalid sector size argument"));
6bcd192c 855 fdisk_save_user_sector_size(cxt, sz, sz);
2b6fc908 856 break;
6bcd192c 857 }
9ff89c5d 858 case 'B':
aeb9a30b
KZ
859 fdisk_enable_bootbits_protection(cxt, 1);
860 break;
0e6f4a20 861 case 'C':
1653f0b0
KZ
862 fdisk_save_user_geometry(cxt,
863 strtou32_or_err(optarg,
864 _("invalid cylinders argument")),
865 0, 0);
0e6f4a20 866 break;
78498b7b 867 case 'c':
852ce62b 868 if (optarg) {
2b0bc17b 869 /* this setting is independent on the current
e3a4aaa7
KZ
870 * actively used label
871 */
872 char *p = *optarg == '=' ? optarg + 1 : optarg;
6a632136 873 struct fdisk_label *lb = fdisk_get_label(cxt, "dos");
e3a4aaa7 874
852ce62b
KZ
875 if (!lb)
876 err(EXIT_FAILURE, _("not found DOS label driver"));
e3a4aaa7 877 if (strcmp(p, "dos") == 0)
852ce62b 878 fdisk_dos_enable_compatible(lb, TRUE);
e3a4aaa7 879 else if (strcmp(p, "nondos") == 0)
852ce62b 880 fdisk_dos_enable_compatible(lb, FALSE);
6e1eda6f
RM
881 else
882 errx(EXIT_FAILURE, _("unknown compatibility mode '%s'"), p);
852ce62b
KZ
883 }
884 /* use default if no optarg specified */
78498b7b 885 break;
0e6f4a20 886 case 'H':
1653f0b0
KZ
887 fdisk_save_user_geometry(cxt, 0,
888 strtou32_or_err(optarg,
889 _("invalid heads argument")),
890 0);
0e6f4a20
KZ
891 break;
892 case 'S':
1653f0b0
KZ
893 fdisk_save_user_geometry(cxt, 0, 0,
894 strtou32_or_err(optarg,
895 _("invalid sectors argument")));
0e6f4a20 896 break;
2b6fc908 897 case 'l':
5b72b3dd 898 act = ACT_LIST;
2b6fc908 899 break;
80a1712f 900 case 'L':
5d51dc2a 901 colormode = UL_COLORMODE_AUTO;
80a1712f
KZ
902 if (optarg)
903 colormode = colormode_or_err(optarg,
904 _("unsupported color mode"));
905 break;
fff8ad58
KZ
906 case 'o':
907 outarg = optarg;
908 break;
2b6fc908 909 case 's':
5b72b3dd 910 act = ACT_SHOWSIZE;
2b6fc908 911 break;
565b7da6
KZ
912 case 't':
913 {
914 struct fdisk_label *lb = NULL;
915
6a632136 916 while (fdisk_next_label(cxt, &lb) == 0)
565b7da6
KZ
917 fdisk_label_set_disabled(lb, 1);
918
6a632136 919 lb = fdisk_get_label(cxt, optarg);
565b7da6
KZ
920 if (!lb)
921 errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg);
922 fdisk_label_set_disabled(lb, 0);
1ce1a112 923 break;
565b7da6 924 }
2b6fc908 925 case 'u':
ec10aa67
KZ
926 if (optarg && *optarg == '=')
927 optarg++;
6a632136 928 if (fdisk_set_unit(cxt, optarg) != 0)
6e1eda6f 929 errx(EXIT_FAILURE, _("unsupported unit"));
2b6fc908 930 break;
e3a4aaa7
KZ
931 case 'V': /* preferred for util-linux */
932 case 'v': /* for backward compatibility only */
a2482eb3
DB
933 printf(UTIL_LINUX_VERSION);
934 return EXIT_SUCCESS;
cb9a4b00
KZ
935 case 'w':
936 wipemode = wipemode_from_string(optarg);
937 if (wipemode < 0)
938 errx(EXIT_FAILURE, _("unsupported wipe mode"));
939 break;
ba465623
KZ
940 case 'W':
941 pwipemode = wipemode_from_string(optarg);
942 if (pwipemode < 0)
943 errx(EXIT_FAILURE, _("unsupported wipe mode"));
944 break;
e1144767 945 case 'h':
6e1eda6f 946 usage();
354f8cc8
KZ
947 case OPT_BYTES:
948 fdisk_set_size_unit(cxt, FDISK_SIZEUNIT_BYTES);
949 break;
2b6fc908 950 default:
677ec86c 951 errtryhelp(EXIT_FAILURE);
2b6fc908 952 }
6dbe3af9 953 }
2b6fc908 954
6bcd192c
KZ
955 if (argc-optind != 1 && fdisk_has_user_device_properties(cxt))
956 warnx(_("The device properties (sector size and geometry) should"
957 " be used with one specified device only."));
7eda085c 958
d0c9ddc3 959 colors_init(colormode, "fdisk");
ffcf0540 960
5b72b3dd
KZ
961 switch (act) {
962 case ACT_LIST:
6a632136 963 fdisk_enable_listonly(cxt, 1);
fff8ad58 964 init_fields(cxt, outarg, NULL);
ffcf0540 965
2b6fc908
KZ
966 if (argc > optind) {
967 int k;
04c163ed 968 int ct = 0;
8fdd483c
KZ
969
970 for (rc = 0, k = optind; k < argc; k++) {
04c163ed
TV
971 if (ct)
972 fputs("\n\n", stdout);
8fdd483c
KZ
973
974 rc += print_device_pt(cxt, argv[k], 1, 0);
04c163ed 975 ct++;
04c163ed 976 }
8fdd483c
KZ
977 if (rc)
978 return EXIT_FAILURE;
ea4824f1 979 } else
d2c47697 980 print_all_devices_pt(cxt, 0);
5b72b3dd 981 break;
2b6fc908 982
5b72b3dd 983 case ACT_SHOWSIZE:
9564e46c 984 /* deprecated */
6e1eda6f
RM
985 if (argc - optind <= 0) {
986 warnx(_("bad usage"));
987 errtryhelp(EXIT_FAILURE);
988 }
6da365de 989 for (i = optind; i < argc; i++) {
0073a4cf
KZ
990 uintmax_t blks = get_dev_blocks(argv[i]);
991
5b72b3dd 992 if (argc - optind == 1)
0073a4cf 993 printf("%ju\n", blks);
2b6fc908 994 else
0073a4cf 995 printf("%s: %ju\n", argv[i], blks);
6dbe3af9 996 }
5b72b3dd 997 break;
6dbe3af9 998
5b72b3dd 999 case ACT_FDISK:
6e1eda6f
RM
1000 if (argc-optind != 1) {
1001 warnx(_("bad usage"));
1002 errtryhelp(EXIT_FAILURE);
1003 }
7eda085c 1004
5b72b3dd 1005 /* Here starts interactive mode, use fdisk_{warn,info,..} functions */
496c979a 1006 color_scheme_enable("welcome", UL_COLOR_GREEN);
09f0c3d9 1007 fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING);
80a1712f
KZ
1008 color_disable();
1009 fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n"
1010 "Be careful before using the write command.\n"));
2d8988bd 1011
6a632136 1012 rc = fdisk_assign_device(cxt, argv[optind], 0);
e146ae4e 1013 if (rc == -EACCES) {
6a632136 1014 rc = fdisk_assign_device(cxt, argv[optind], 1);
e146ae4e 1015 if (rc == 0)
62eea9ce 1016 fdisk_warnx(cxt, _("Device is open in read-only mode."));
e146ae4e
KZ
1017 }
1018 if (rc)
2d8988bd
KZ
1019 err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
1020
5b72b3dd 1021 fflush(stdout);
56c07b96 1022
5635d195
KZ
1023 if (fdisk_get_collision(cxt))
1024 follow_wipe_mode(cxt);
cb9a4b00 1025
aa36c2cf 1026 if (!fdisk_has_label(cxt)) {
2d8988bd 1027 fdisk_info(cxt, _("Device does not contain a recognized partition table."));
5b72b3dd 1028 fdisk_create_disklabel(cxt, NULL);
433d05ff 1029
aa36c2cf 1030 } else if (fdisk_is_label(cxt, GPT) && fdisk_gpt_is_hybrid(cxt))
433d05ff 1031 fdisk_warnx(cxt, _(
09af3db4 1032 "A hybrid GPT was detected. You have to sync "
433d05ff 1033 "the hybrid MBR manually (expert command 'M')."));
6dbe3af9 1034
fff8ad58
KZ
1035 init_fields(cxt, outarg, NULL); /* -o <columns> */
1036
fadd8e08
KZ
1037 if (!fdisk_is_readonly(cxt)) {
1038 fdisk_get_partitions(cxt, &original_layout);
1039 device_is_used = fdisk_device_is_used(cxt);
1040 }
1041
5b72b3dd
KZ
1042 while (1)
1043 process_fdisk_menu(&cxt);
1044 }
9777759a 1045
c7119037 1046 fdisk_unref_context(cxt);
5b72b3dd 1047 return EXIT_SUCCESS;
6dbe3af9 1048}