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