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