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