]> git.ipfire.org Git - thirdparty/util-linux.git/blame - fdisks/fdisk.c
fdisk: remove global unit variables
[thirdparty/util-linux.git] / fdisks / fdisk.c
CommitLineData
6dbe3af9
KZ
1/* fdisk.c -- Partition table manipulator for Linux.
2 *
3 * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
38b36353 4 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
6dbe3af9
KZ
5 *
6 * This program is free software. You can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation: either version 1 or
9 * (at your option) any later version.
6dbe3af9
KZ
10 */
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
52b38677 25#include "xalloc.h"
22853e4a 26#include "nls.h"
e66ac5d3 27#include "rpmatch.h"
810f986b 28#include "blkdev.h"
7eda085c 29#include "common.h"
b8d22034 30#include "mbsalign.h"
726f69e2 31#include "fdisk.h"
929f243f 32#include "wholedisk.h"
6bec8710
KZ
33#include "pathnames.h"
34#include "canonicalize.h"
20aa2570 35#include "strutils.h"
b2d28533 36#include "closestream.h"
5c36a0eb 37
2b6fc908 38#include "fdisksunlabel.h"
5c36a0eb
KZ
39#include "fdisksgilabel.h"
40#include "fdiskaixlabel.h"
e7fa917a 41#include "fdiskmaclabel.h"
e2ee9178 42#include "fdiskdoslabel.h"
37eadc17 43#include "fdiskbsdlabel.h"
5c36a0eb 44
48d7b13a
KZ
45#ifdef HAVE_LINUX_COMPILER_H
46#include <linux/compiler.h>
47#endif
48#ifdef HAVE_LINUX_BLKPG_H
7eda085c
KZ
49#include <linux/blkpg.h>
50#endif
6dbe3af9 51
c482303e
FC
52/* menu list description */
53
54struct menulist_descr {
55 char command; /* command key */
56 char *description; /* command description */
79c8a145 57 enum fdisk_labeltype label[2]; /* disklabel types associated with main and expert menu */
c482303e
FC
58};
59
60static const struct menulist_descr menulist[] = {
bbc41eeb
KZ
61 {'a', N_("change number of alternate cylinders"), {0, FDISK_DISKLABEL_SUN}},
62 {'a', N_("select bootable partition"), {FDISK_DISKLABEL_SGI, 0}},
ff5775bd
DB
63 {'a', N_("toggle a bootable flag"), {FDISK_DISKLABEL_DOS, 0}},
64 {'a', N_("toggle a read only flag"), {FDISK_DISKLABEL_SUN, 0}},
ff5775bd 65 {'b', N_("edit bootfile entry"), {FDISK_DISKLABEL_SGI, 0}},
bbc41eeb 66 {'b', N_("edit bsd disklabel"), {FDISK_DISKLABEL_DOS, 0}},
ff5775bd 67 {'b', N_("move beginning of data in a partition"), {0, FDISK_DISKLABEL_DOS}},
bbc41eeb
KZ
68 {'c', N_("change number of cylinders"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}},
69 {'c', N_("select sgi swap partition"), {FDISK_DISKLABEL_SGI, 0}},
ff5775bd
DB
70 {'c', N_("toggle the dos compatibility flag"), {FDISK_DISKLABEL_DOS, 0}},
71 {'c', N_("toggle the mountable flag"), {FDISK_DISKLABEL_SUN, 0}},
ff5775bd
DB
72 {'d', N_("delete a partition"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}},
73 {'d', N_("print the raw data in the partition table"), {0, FDISK_DISKLABEL_ANY}},
74 {'e', N_("change number of extra sectors per cylinder"), {0, FDISK_DISKLABEL_SUN}},
ff5775bd 75 {'e', N_("edit drive data"), {FDISK_DISKLABEL_OSF, 0}},
bbc41eeb 76 {'e', N_("list extended partitions"), {0, FDISK_DISKLABEL_DOS}},
ff5775bd 77 {'f', N_("fix partition order"), {0, FDISK_DISKLABEL_DOS}},
ff5775bd 78 {'g', N_("create a new empty GPT partition table"), {~FDISK_DISKLABEL_OSF, 0}},
bbc41eeb
KZ
79 {'g', N_("create an IRIX (SGI) partition table"), {0, FDISK_DISKLABEL_ANY}}, /* for backward compatibility only */
80 {'G', N_("create an IRIX (SGI) partition table"), {~FDISK_DISKLABEL_OSF, 0}},
ff5775bd
DB
81 {'h', N_("change number of heads"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}},
82 {'i', N_("change interleave factor"), {0, FDISK_DISKLABEL_SUN}},
83 {'i', N_("change the disk identifier"), {0, FDISK_DISKLABEL_DOS}},
84 {'i', N_("install bootstrap"), {FDISK_DISKLABEL_OSF, 0}},
85 {'l', N_("list known partition types"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}},
86 {'m', N_("print this menu"), {FDISK_DISKLABEL_ANY, FDISK_DISKLABEL_ANY}},
87 {'n', N_("add a new partition"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}},
ff5775bd 88 {'o', N_("change rotation speed (rpm)"), {0, FDISK_DISKLABEL_SUN}},
bbc41eeb 89 {'o', N_("create a new empty DOS partition table"), {~FDISK_DISKLABEL_OSF, 0}},
ff5775bd
DB
90 {'p', N_("print the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}},
91 {'q', N_("quit without saving changes"), {FDISK_DISKLABEL_ANY, FDISK_DISKLABEL_ANY}},
92 {'r', N_("return to main menu"), {FDISK_DISKLABEL_OSF, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF}},
ff5775bd 93 {'s', N_("change number of sectors/track"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}},
bbc41eeb 94 {'s', N_("create a new empty Sun disklabel"), {~FDISK_DISKLABEL_OSF, 0}},
ff5775bd
DB
95 {'s', N_("show complete disklabel"), {FDISK_DISKLABEL_OSF, 0}},
96 {'t', N_("change a partition's system id"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}},
97 {'u', N_("change display/entry units"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}},
98 {'v', N_("verify the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI}},
ff5775bd 99 {'w', N_("write disklabel to disk"), {FDISK_DISKLABEL_OSF, 0}},
bbc41eeb 100 {'w', N_("write table to disk and exit"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_GPT, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI}},
ff5775bd 101 {'x', N_("extra functionality (experts only)"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI, 0}},
c482303e 102#if !defined (__alpha__)
ff5775bd 103 {'x', N_("link BSD partition to non-BSD partition"), {FDISK_DISKLABEL_OSF, 0}},
c482303e 104#endif
ff5775bd 105 {'y', N_("change number of physical cylinders"), {0, FDISK_DISKLABEL_SUN}},
c482303e
FC
106};
107
749af4b6
KZ
108
109
50dec1eb 110sector_t get_nr_sects(struct partition *p) {
2b6fc908
KZ
111 return read4_little_endian(p->size4);
112}
113
823f0fd1 114char *line_ptr, /* interactive input */
22853e4a 115 line_buffer[LINE_LENGTH];
6dbe3af9 116
e3661531 117int nowarn = 0; /* no warnings for fdisk -l/-s */
6dbe3af9 118
df1dddf9 119unsigned int user_cylinders, user_heads, user_sectors;
365acc97 120
ec10aa67
KZ
121void toggle_units(struct fdisk_context *cxt)
122{
123 fdisk_context_set_unit(cxt,
124 fdisk_context_use_cylinders(cxt) ? "sectors" :
125 "cylinders");
126 if (fdisk_context_use_cylinders(cxt))
127 fdisk_info(cxt, _("Changing display/entry units to cylinders (DEPRECATED!)."));
128 else
129 fdisk_info(cxt, _("Changing display/entry units to sectors."));
130}
131
132
7c1db6b4
FC
133static void __attribute__ ((__noreturn__)) usage(FILE *out)
134{
135 fprintf(out, _("Usage:\n"
136 " %1$s [options] <disk> change partition table\n"
137 " %1$s [options] -l <disk> list partition table(s)\n"
138 " %1$s -s <partition> give partition size(s) in blocks\n"
139 "\nOptions:\n"
140 " -b <size> sector size (512, 1024, 2048 or 4096)\n"
141 " -c[=<mode>] compatible mode: 'dos' or 'nondos' (default)\n"
142 " -h print this help text\n"
143 " -u[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"
144 " -v print program version\n"
145 " -C <number> specify the number of cylinders\n"
146 " -H <number> specify the number of heads\n"
147 " -S <number> specify the number of sectors per track\n"
148 "\n"), program_invocation_short_name);
149 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
150}
151
888a2e1c
SK
152void __attribute__((__noreturn__))
153fatal(struct fdisk_context *cxt, enum failure why)
9f6c866f 154{
823f0fd1 155 close(cxt->dev_fd);
6dbe3af9 156 switch (why) {
6dbe3af9 157 case unable_to_read:
823f0fd1 158 err(EXIT_FAILURE, _("unable to read %s"), cxt->dev_path);
c845f12c 159
6dbe3af9 160 case unable_to_seek:
823f0fd1 161 err(EXIT_FAILURE, _("unable to seek on %s"), cxt->dev_path);
c845f12c 162
6dbe3af9 163 case unable_to_write:
823f0fd1 164 err(EXIT_FAILURE, _("unable to write %s"), cxt->dev_path);
c845f12c 165
7eda085c 166 case ioctl_error:
823f0fd1 167 err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), cxt->dev_path);
c845f12c 168
c07ebfa1 169 default:
c845f12c 170 err(EXIT_FAILURE, _("fatal error"));
6dbe3af9 171 }
6dbe3af9
KZ
172}
173
22853e4a
KZ
174struct partition *
175get_part_table(int i) {
176 return ptes[i].part_table;
177}
178
ff5775bd 179void print_menu(struct fdisk_context *cxt, enum menutype menu)
c482303e
FC
180{
181 size_t i;
53b422ab 182 int id;
6dbe3af9 183
c482303e
FC
184 puts(_("Command action"));
185
53b422ab
KZ
186 id = cxt && cxt->label ? cxt->label->id : FDISK_DISKLABEL_ANY;
187
c482303e 188 for (i = 0; i < ARRAY_SIZE(menulist); i++)
53b422ab 189 if (menulist[i].label[menu] & id)
c482303e 190 printf(" %c %s\n", menulist[i].command, menulist[i].description);
6dbe3af9
KZ
191}
192
7b575fcc 193void list_partition_types(struct fdisk_context *cxt)
6dbe3af9 194{
7b575fcc 195 struct fdisk_parttype *types;
6dbe3af9
KZ
196 int i;
197
7b575fcc
KZ
198 if (!cxt || !cxt->label || !cxt->label->parttypes)
199 return;
2b6fc908 200
7b575fcc 201 types = cxt->label->parttypes;
6dbe3af9 202
7b575fcc
KZ
203 if (types[0].typestr == NULL) {
204 /*
205 * Prints in 4 columns in format <hex> <name>
206 */
207 unsigned int last[4], done = 0, next = 0, size;
b8d22034 208
7b575fcc
KZ
209 for (i = 0; types[i].name; i++);
210 size = i;
211
212 for (i = 3; i >= 0; i--)
213 last[3 - i] = done += (size + i - done) / (i + 1);
214 i = done = 0;
215
216 do {
217 #define NAME_WIDTH 15
218 char name[NAME_WIDTH * MB_LEN_MAX];
219 size_t width = NAME_WIDTH;
220 struct fdisk_parttype *t = &types[next];
221 size_t ret;
222
223 printf("%c%2x ", i ? ' ' : '\n', t->type);
224 ret = mbsalign(_(t->name), name, sizeof(name),
225 &width, MBS_ALIGN_LEFT, 0);
226
227 if (ret == (size_t)-1 || ret >= sizeof(name))
228 printf("%-15.15s", _(t->name));
229 else
230 fputs(name, stdout);
231
232 next = last[i++] + done;
233 if (i > 3 || next >= last[i]) {
234 i = 0;
235 next = ++done;
236 }
237 } while (done < last[0]);
238
239 } else {
240 /*
241 * Prints 1 column in format <idx> <name> <typestr>
242 */
243 struct fdisk_parttype *t;
244
245 for (i = 0, t = types; t->name; t++, i++)
246 printf("%3d %-30s %s\n", i + 1, t->name, t->typestr);
247 }
6dbe3af9
KZ
248 putchar('\n');
249}
250
22853e4a
KZ
251static int
252test_c(char **m, char *mesg) {
6dbe3af9
KZ
253 int val = 0;
254 if (!*m)
7eda085c 255 fprintf(stderr, _("You must set"));
6dbe3af9
KZ
256 else {
257 fprintf(stderr, " %s", *m);
258 val = 1;
259 }
260 *m = mesg;
261 return val;
262}
263
24cd580b 264int warn_geometry(struct fdisk_context *cxt)
e2ee9178 265{
6dbe3af9
KZ
266 char *m = NULL;
267 int prev = 0;
a47f2e66 268
ff5775bd 269 if (fdisk_is_disklabel(cxt, SGI)) /* cannot set cylinders etc anyway */
a47f2e66 270 return 0;
24cd580b 271 if (!cxt->geom.heads)
7eda085c 272 prev = test_c(&m, _("heads"));
24cd580b 273 if (!cxt->geom.sectors)
7eda085c 274 prev = test_c(&m, _("sectors"));
24cd580b 275 if (!cxt->geom.cylinders)
7eda085c 276 prev = test_c(&m, _("cylinders"));
6dbe3af9
KZ
277 if (!m)
278 return 0;
279 fprintf(stderr,
7eda085c
KZ
280 _("%s%s.\nYou can do this from the extra functions menu.\n"),
281 prev ? _(" and ") : " ", m);
6dbe3af9
KZ
282 return 1;
283}
284
e53ced85 285void warn_limits(struct fdisk_context *cxt)
e2ee9178 286{
618882d6
DB
287 if (cxt->total_sectors > UINT_MAX && !nowarn) {
288 unsigned long long bytes = cxt->total_sectors * cxt->sector_size;
06023c2e 289 int giga = bytes / 1000000000;
32b40fec
KZ
290 int hectogiga = (giga + 50) / 100;
291
ee5355e0
KZ
292 fprintf(stderr, _("\n"
293"WARNING: The size of this disk is %d.%d TB (%llu bytes).\n"
32b40fec 294"DOS partition table format can not be used on drives for volumes\n"
e53ced85 295"larger than (%llu bytes) for %ld-byte sectors. Use parted(1) and GUID \n"
ee5355e0 296"partition table format (GPT).\n\n"),
32b40fec 297 hectogiga / 10, hectogiga % 10,
06023c2e 298 bytes,
e53ced85
DB
299 (sector_t ) UINT_MAX * cxt->sector_size,
300 cxt->sector_size);
ee5355e0 301 }
6dbe3af9
KZ
302}
303
bddd84e7 304static void maybe_exit(struct fdisk_context *cxt, int rc, int *asked)
aead9d13
FC
305{
306 char line[LINE_LENGTH];
307
bddd84e7
KZ
308 assert(cxt);
309 assert(cxt->label);
310
aead9d13 311 putchar('\n');
8feb31e5
KZ
312 if (asked)
313 *asked = 0;
aead9d13 314
bddd84e7 315 if (fdisk_label_is_changed(cxt->label)) {
aead9d13
FC
316 fprintf(stderr, _("Do you really want to quit? "));
317
318 if (!fgets(line, LINE_LENGTH, stdin) || rpmatch(line) == 1)
bddd84e7 319 goto leave;
8feb31e5
KZ
320 if (asked)
321 *asked = 1;
bddd84e7
KZ
322 return;
323 }
324leave:
325 fdisk_free_context(cxt);
326 exit(rc);
aead9d13
FC
327}
328
5c36a0eb 329/* read line; return 0 or first char */
bddd84e7 330int read_line(struct fdisk_context *cxt, int *asked)
6dbe3af9 331{
5c36a0eb 332 line_ptr = line_buffer;
7eda085c 333 if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
bddd84e7 334 maybe_exit(cxt, 1, asked);
6dbe3af9 335 return 0;
7eda085c 336 }
8feb31e5
KZ
337 if (asked)
338 *asked = 0;
6dbe3af9
KZ
339 while (*line_ptr && !isgraph(*line_ptr))
340 line_ptr++;
341 return *line_ptr;
342}
343
bddd84e7 344char read_char(struct fdisk_context *cxt, char *mesg)
6dbe3af9 345{
2b6fc908 346 do {
6dbe3af9 347 fputs(mesg, stdout);
66ee8158 348 fflush (stdout); /* requested by niles@scyld.com */
bddd84e7
KZ
349
350 } while (!read_line(cxt, NULL));
351
6dbe3af9
KZ
352 return *line_ptr;
353}
354
bddd84e7 355char read_chars(struct fdisk_context *cxt, char *mesg)
2b6fc908 356{
8feb31e5
KZ
357 int rc, asked = 0;
358
359 do {
360 fputs(mesg, stdout);
361 fflush (stdout); /* niles@scyld.com */
bddd84e7 362 rc = read_line(cxt, &asked);
8feb31e5
KZ
363 } while (asked);
364
365 if (!rc) {
2b6fc908 366 *line_ptr = '\n';
5c36a0eb
KZ
367 line_ptr[1] = 0;
368 }
369 return *line_ptr;
2b6fc908
KZ
370}
371
559d921e 372struct fdisk_parttype *read_partition_type(struct fdisk_context *cxt)
6dbe3af9 373{
559d921e
KZ
374 if (!cxt || !cxt->label || !cxt->label->nparttypes)
375 return NULL;
376
377 do {
378 size_t sz;
379
380 if (cxt->label->parttypes[0].typestr)
bddd84e7 381 read_chars(cxt, _("Partition type (type L to list all types): "));
559d921e 382 else
bddd84e7 383 read_chars(cxt, _("Hex code (type L to list all codes): "));
559d921e
KZ
384
385 sz = strlen(line_ptr);
386 if (!sz || line_ptr[sz - 1] != '\n' || sz == 1)
387 continue;
388 line_ptr[sz - 1] = '\0';
389
390 if (tolower(*line_ptr) == 'l')
391 list_partition_types(cxt);
392 else
393 return fdisk_parse_parttype(cxt, line_ptr);
394 } while (1);
395
396 return NULL;
726f69e2
KZ
397}
398
559d921e 399
9dea2923 400unsigned int
e53ced85
DB
401read_int_with_suffix(struct fdisk_context *cxt,
402 unsigned int low, unsigned int dflt, unsigned int high,
2c911261 403 unsigned int base, char *mesg, int *is_suffix_used)
726f69e2 404{
2c911261 405 unsigned int res;
5c36a0eb 406 int default_ok = 1;
0c381880 407 int absolute = 0;
5c36a0eb 408 static char *ms = NULL;
e99da659 409 static size_t mslen = 0;
5c36a0eb 410
c07ebfa1
KZ
411 if (!ms || strlen(mesg)+100 > mslen) {
412 mslen = strlen(mesg)+200;
52b38677 413 ms = xrealloc(ms,mslen);
726f69e2 414 }
6dbe3af9 415
5c36a0eb
KZ
416 if (dflt < low || dflt > high)
417 default_ok = 0;
418
419 if (default_ok)
df1dddf9 420 snprintf(ms, mslen, _("%s (%u-%u, default %u): "),
c07ebfa1 421 mesg, low, high, dflt);
5c36a0eb 422 else
df1dddf9 423 snprintf(ms, mslen, "%s (%u-%u): ",
c07ebfa1 424 mesg, low, high);
5c36a0eb 425
6dbe3af9 426 while (1) {
5c36a0eb
KZ
427 int use_default = default_ok;
428
429 /* ask question and read answer */
bddd84e7 430 while (read_chars(cxt, ms) != '\n' && !isdigit(*line_ptr)
5c36a0eb
KZ
431 && *line_ptr != '-' && *line_ptr != '+')
432 continue;
433
726f69e2 434 if (*line_ptr == '+' || *line_ptr == '-') {
a5a16c68 435 int minus = (*line_ptr == '-');
b1edb510 436 int suflen;
a5a16c68 437
0c381880 438 absolute = 0;
2c911261 439 res = atoi(line_ptr + 1);
a5a16c68 440
b1edb510 441 while (isdigit(*++line_ptr))
726f69e2 442 use_default = 0;
a5a16c68 443
74396048
FC
444 while (isspace(*line_ptr))
445 line_ptr++;
446
b1edb510
KZ
447 suflen = strlen(line_ptr) - 1;
448
449 while(isspace(*(line_ptr + suflen)))
450 *(line_ptr + suflen--) = '\0';
451
452 if ((*line_ptr == 'C' || *line_ptr == 'c') &&
453 *(line_ptr + 1) == '\0') {
454 /*
455 * Cylinders
456 */
ec10aa67 457 if (fdisk_context_use_cylinders(cxt))
24cd580b 458 res *= cxt->geom.heads * cxt->geom.sectors;
5ea8931c
KZ
459 } else if (*line_ptr &&
460 *(line_ptr + 1) == 'B' &&
b1edb510
KZ
461 *(line_ptr + 2) == '\0') {
462 /*
463 * 10^N
464 */
465 if (*line_ptr == 'K')
a5a16c68 466 absolute = 1000;
b1edb510 467 else if (*line_ptr == 'M')
a5a16c68 468 absolute = 1000000;
b1edb510 469 else if (*line_ptr == 'G')
a5a16c68 470 absolute = 1000000000;
b1edb510
KZ
471 else
472 absolute = -1;
5ea8931c
KZ
473 } else if (*line_ptr &&
474 *(line_ptr + 1) == '\0') {
b1edb510
KZ
475 /*
476 * 2^N
477 */
478 if (*line_ptr == 'K')
479 absolute = 1 << 10;
480 else if (*line_ptr == 'M')
481 absolute = 1 << 20;
482 else if (*line_ptr == 'G')
483 absolute = 1 << 30;
484 else
485 absolute = -1;
486 } else if (*line_ptr != '\0')
487 absolute = -1;
488
489 if (absolute == -1) {
490 printf(_("Unsupported suffix: '%s'.\n"), line_ptr);
491 printf(_("Supported: 10^N: KB (KiloByte), MB (MegaByte), GB (GigaByte)\n"
492 " 2^N: K (KibiByte), M (MebiByte), G (GibiByte)\n"));
493 continue;
726f69e2 494 }
b1edb510 495
2c911261 496 if (absolute && res) {
a5a16c68
KZ
497 unsigned long long bytes;
498 unsigned long unit;
499
2c911261 500 bytes = (unsigned long long) res * absolute;
ec10aa67 501 unit = cxt->sector_size * fdisk_context_get_units_per_sector(cxt);
a5a16c68
KZ
502 bytes += unit/2; /* round */
503 bytes /= unit;
2c911261 504 res = bytes;
a5a16c68
KZ
505 }
506 if (minus)
2c911261
FC
507 res = -res;
508 res += base;
5c36a0eb 509 } else {
2c911261 510 res = atoi(line_ptr);
5c36a0eb 511 while (isdigit(*line_ptr)) {
726f69e2
KZ
512 line_ptr++;
513 use_default = 0;
514 }
515 }
0c381880
FC
516 if (use_default) {
517 printf(_("Using default value %u\n"), dflt);
518 return dflt;
519 }
2c911261 520 if (res >= low && res <= high)
6dbe3af9 521 break;
726f69e2 522 else
7eda085c 523 printf(_("Value out of range.\n"));
6dbe3af9 524 }
0c381880
FC
525 if (is_suffix_used)
526 *is_suffix_used = absolute > 0;
2c911261 527 return res;
6dbe3af9
KZ
528}
529
b7e76f8e
KZ
530/*
531 * Print the message MESG, then read an integer in LOW..HIGH.
532 * If the user hits Enter, DFLT is returned, provided that is in LOW..HIGH.
533 * Answers like +10 are interpreted as offsets from BASE.
534 *
535 * There is no default if DFLT is not between LOW and HIGH.
536 */
537unsigned int
e53ced85
DB
538read_int(struct fdisk_context *cxt,
539 unsigned int low, unsigned int dflt, unsigned int high,
b7e76f8e
KZ
540 unsigned int base, char *mesg)
541{
e53ced85 542 return read_int_with_suffix(cxt, low, dflt, high, base, mesg, NULL);
b7e76f8e
KZ
543}
544
22853e4a 545int
e53ced85 546get_partition_dflt(struct fdisk_context *cxt, int warn, int max, int dflt) {
22853e4a
KZ
547 struct pte *pe;
548 int i;
549
e53ced85 550 i = read_int(cxt, 1, dflt, max, 0, _("Partition number")) - 1;
22853e4a 551 pe = &ptes[i];
6dbe3af9 552
ff5775bd
DB
553 if (warn && !fdisk_is_disklabel(cxt, GPT)) {
554 if ((!fdisk_is_disklabel(cxt, SUN) &&
555 !fdisk_is_disklabel(cxt, SGI) && !pe->part_table->sys_ind)
556 || (fdisk_is_disklabel(cxt, SUN) &&
d6ab93f1 557 sun_is_empty_type(cxt, i))
ff5775bd
DB
558 || (fdisk_is_disklabel(cxt, SGI) &&
559 (!sgi_get_num_sectors(cxt, i))))
24f4bbff
KZ
560 fprintf(stderr,
561 _("Warning: partition %d has empty type\n"),
562 i+1);
563 }
6dbe3af9
KZ
564 return i;
565}
566
147e1e73 567int
e53ced85
DB
568get_partition(struct fdisk_context *cxt, int warn, int max) {
569 return get_partition_dflt(cxt, warn, max, 0);
147e1e73
KZ
570}
571
b152082d
FC
572/* User partition selection unless one partition only is available */
573
24f4bbff 574static int
e53ced85 575get_existing_partition(struct fdisk_context *cxt, int warn, int max) {
24f4bbff
KZ
576 int pno = -1;
577 int i;
578
ff5775bd 579 if (!fdisk_is_disklabel(cxt, DOS))
b152082d
FC
580 goto not_implemented;
581
24f4bbff
KZ
582 for (i = 0; i < max; i++) {
583 struct pte *pe = &ptes[i];
584 struct partition *p = pe->part_table;
585
586 if (p && !is_cleared_partition(p)) {
587 if (pno >= 0)
588 goto not_unique;
589 pno = i;
590 }
591 }
365acc97 592
24f4bbff
KZ
593 if (pno >= 0) {
594 printf(_("Selected partition %d\n"), pno+1);
595 return pno;
596 }
597 printf(_("No partition is defined yet!\n"));
598 return -1;
599
b152082d
FC
600not_implemented:
601not_unique:
e53ced85 602 return get_partition(cxt, warn, max);
24f4bbff
KZ
603}
604
852ce62b
KZ
605static void toggle_dos_compatibility_flag(struct fdisk_context *cxt)
606{
607 struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos");
608 int flag;
609
610 if (!lb)
611 return;
612
613 flag = !fdisk_dos_is_compatible(lb);
614
615 if (flag)
278f63c0 616 printf(_("DOS Compatibility flag is set (DEPRECATED!)\n"));
edd7b958 617 else
7eda085c 618 printf(_("DOS Compatibility flag is not set\n"));
edd7b958 619
852ce62b
KZ
620 fdisk_dos_enable_compatible(lb, flag);
621
622 if (fdisk_is_disklabel(cxt, DOS))
623 fdisk_reset_alignment(cxt);
6dbe3af9
KZ
624}
625
61c4cb85 626static void delete_partition(struct fdisk_context *cxt, int partnum)
47104bae 627{
61c4cb85 628 if (partnum < 0 || warn_geometry(cxt))
4a96a62a
FC
629 return;
630
61c4cb85 631 ptes[partnum].changed = 1;
1f5eb51b
DB
632 if (fdisk_delete_partition(cxt, partnum) != 0)
633 printf(_("Could not delete partition %d\n"), partnum + 1);
634 else
635 printf(_("Partition %d is deleted\n"), partnum + 1);
6dbe3af9
KZ
636}
637
02460b8a 638static void change_partition_type(struct fdisk_context *cxt)
e53ced85 639{
559d921e
KZ
640 int i;
641 struct fdisk_parttype *t, *org_t;
6dbe3af9 642
e3661531
KZ
643 assert(cxt);
644 assert(cxt->label);
645
646 i = get_existing_partition(cxt, 0, cxt->label->nparts_max);
24f4bbff
KZ
647 if (i == -1)
648 return;
2b6fc908 649
010186f2
KZ
650 org_t = t = fdisk_get_partition_type(cxt, i);
651 if (!t)
7eda085c 652 printf(_("Partition %d does not exist yet!\n"), i + 1);
010186f2 653
02460b8a 654 else do {
559d921e 655 t = read_partition_type(cxt);
559d921e
KZ
656 if (!t)
657 continue;
658
02460b8a
KZ
659 if (fdisk_set_partition_type(cxt, i, t) == 0) {
660 ptes[i].changed = 1;
661 printf (_("Changed type of partition '%s' to '%s'\n"),
662 org_t ? org_t->name : _("Unknown"),
663 t ? t->name : _("Unknown"));
664 } else {
665 printf (_("Type of partition %d is unchanged: %s\n"),
666 i + 1,
667 org_t ? org_t->name : _("Unknown"));
6dbe3af9 668 }
02460b8a
KZ
669 break;
670 } while (1);
559d921e
KZ
671
672 fdisk_free_parttype(t);
673 fdisk_free_parttype(org_t);
6dbe3af9
KZ
674}
675
22853e4a 676static void
7737f698 677list_disk_geometry(struct fdisk_context *cxt) {
618882d6 678 unsigned long long bytes = cxt->total_sectors * cxt->sector_size;
24f4bbff
KZ
679 long megabytes = bytes/1000000;
680
681 if (megabytes < 10000)
f8527f4b 682 printf(_("\nDisk %s: %ld MB, %lld bytes"),
823f0fd1 683 cxt->dev_path, megabytes, bytes);
32b40fec
KZ
684 else {
685 long hectomega = (megabytes + 50) / 100;
f8527f4b 686 printf(_("\nDisk %s: %ld.%ld GB, %llu bytes"),
823f0fd1 687 cxt->dev_path, hectomega / 10, hectomega % 10, bytes);
32b40fec 688 }
f8527f4b 689 printf(_(", %llu sectors\n"), cxt->total_sectors);
852ce62b 690 if (is_dos_compatible(cxt))
a7a6f7d2
PU
691 printf(_("%d heads, %llu sectors/track, %llu cylinders\n"),
692 cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders);
e53ced85 693 printf(_("Units = %s of %d * %ld = %ld bytes\n"),
ec10aa67
KZ
694 fdisk_context_get_unit(cxt, PLURAL),
695 fdisk_context_get_units_per_sector(cxt),
696 cxt->sector_size,
697 fdisk_context_get_units_per_sector(cxt) * cxt->sector_size);
05dc9645 698
e53ced85
DB
699 printf(_("Sector size (logical/physical): %lu bytes / %lu bytes\n"),
700 cxt->sector_size, cxt->phy_sector_size);
a8a86a1c 701 printf(_("I/O size (minimum/optimal): %lu bytes / %lu bytes\n"),
e53ced85
DB
702 cxt->min_io_size, cxt->io_size);
703 if (cxt->alignment_offset)
704 printf(_("Alignment offset: %lu bytes\n"), cxt->alignment_offset);
b546d442
KZ
705 if (fdisk_dev_has_disklabel(cxt))
706 printf(_("Disk label type: %s\n"), cxt->label->name);
ff5775bd 707 if (fdisk_is_disklabel(cxt, DOS))
38b36353 708 dos_print_mbr_id(cxt);
bb6aacfe 709 printf("\n");
5c36a0eb
KZ
710}
711
766d5156
DB
712static void list_table(struct fdisk_context *cxt, int xtra)
713{
ff5775bd 714 if (fdisk_is_disklabel(cxt, SUN)) {
7737f698 715 sun_list_table(cxt, xtra);
5c36a0eb
KZ
716 return;
717 }
718
ff5775bd 719 if (fdisk_is_disklabel(cxt, SGI)) {
7737f698 720 sgi_list_table(cxt, xtra);
5c36a0eb
KZ
721 return;
722 }
2b6fc908 723
7737f698 724 list_disk_geometry(cxt);
22853e4a 725
ff5775bd 726 if (fdisk_is_disklabel(cxt, GPT)) {
766d5156
DB
727 gpt_list_table(cxt, xtra);
728 return;
729 }
730
ff5775bd 731 if (fdisk_is_disklabel(cxt, OSF)) {
7737f698 732 xbsd_print_disklabel(cxt, xtra);
22853e4a
KZ
733 return;
734 }
735
6d864a49
KZ
736 if (fdisk_is_disklabel(cxt, DOS))
737 dos_list_table(cxt, xtra);
6dbe3af9
KZ
738}
739
2ca61a61
DB
740static void verify(struct fdisk_context *cxt)
741{
24cd580b 742 if (warn_geometry(cxt))
6dbe3af9
KZ
743 return;
744
2ca61a61 745 fdisk_verify_disklabel(cxt);
6dbe3af9
KZ
746}
747
e53ced85
DB
748void print_partition_size(struct fdisk_context *cxt,
749 int num, sector_t start, sector_t stop, int sysid)
20aa2570
FC
750{
751 char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
7e6e9c70 752 (uint64_t)(stop - start + 1) * cxt->sector_size);
559d921e
KZ
753 struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid);
754
755 printf(_("Partition %d of type %s and of size %s is set\n"),
756 num, t ? t->name : _("Unknown"), str);
20aa2570
FC
757 free(str);
758}
759
e53ced85 760static void new_partition(struct fdisk_context *cxt)
9dea2923 761{
9fcd49d5
KZ
762 assert(cxt);
763 assert(cxt->label);
764
0f639e54 765 if (warn_geometry(cxt))
e8f26419 766 return;
e8f26419 767
416c43a9 768 fdisk_add_partition(cxt, NULL);
6dbe3af9
KZ
769}
770
fae7b1bc
DB
771static void write_table(struct fdisk_context *cxt)
772{
773 int rc;
6dbe3af9 774
fae7b1bc
DB
775 rc = fdisk_write_disklabel(cxt);
776 if (rc)
777 err(EXIT_FAILURE, _("cannot write disk label"));
6dbe3af9 778
7eda085c 779 printf(_("The partition table has been altered!\n\n"));
7737f698 780 reread_partition_table(cxt, 1);
eb63b9b8
KZ
781}
782
783void
7737f698 784reread_partition_table(struct fdisk_context *cxt, int leave) {
eb63b9b8 785 int i;
edd867fa 786 struct stat statbuf;
6dbe3af9 787
823f0fd1 788 i = fstat(cxt->dev_fd, &statbuf);
edd867fa 789 if (i == 0 && S_ISBLK(statbuf.st_mode)) {
edd867fa 790 sync();
6b0054a2
ST
791#ifdef BLKRRPART
792 printf(_("Calling ioctl() to re-read partition table.\n"));
823f0fd1 793 i = ioctl(cxt->dev_fd, BLKRRPART);
6b0054a2
ST
794#else
795 errno = ENOSYS;
796 i = 1;
797#endif
726f69e2
KZ
798 }
799
e8f26419 800 if (i) {
960cf573 801 printf(_("\nWARNING: Re-reading the partition table failed with error %d: %m.\n"
c64061c9
VD
802 "The kernel still uses the old table. The new table will be used at\n"
803 "the next reboot or after you run partprobe(8) or kpartx(8)\n"),
960cf573 804 errno);
e8f26419 805 }
6dbe3af9 806
eb63b9b8 807 if (leave) {
823f0fd1 808 if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
a47f2e66
KZ
809 fprintf(stderr, _("\nError closing file\n"));
810 exit(1);
811 }
eb63b9b8
KZ
812
813 printf(_("Syncing disks.\n"));
814 sync();
eb63b9b8
KZ
815 exit(!!i);
816 }
6dbe3af9
KZ
817}
818
819#define MAX_PER_LINE 16
22853e4a 820static void
e53ced85 821print_buffer(struct fdisk_context *cxt, unsigned char pbuffer[]) {
e99da659 822 unsigned int i, l;
6dbe3af9 823
e53ced85 824 for (i = 0, l = 0; i < cxt->sector_size; i++, l++) {
6dbe3af9
KZ
825 if (l == 0)
826 printf("0x%03X:", i);
be97c5f3 827 printf(" %02X", pbuffer[i]);
6dbe3af9
KZ
828 if (l == MAX_PER_LINE - 1) {
829 printf("\n");
830 l = -1;
831 }
832 }
833 if (l > 0)
834 printf("\n");
835 printf("\n");
836}
837
e53ced85
DB
838static void print_raw(struct fdisk_context *cxt)
839{
e3661531
KZ
840 size_t i;
841
842 assert(cxt);
843 assert(cxt->label);
6dbe3af9 844
e53ced85 845 printf(_("Device: %s\n"), cxt->dev_path);
ff5775bd
DB
846 if (fdisk_is_disklabel(cxt, SUN) ||
847 fdisk_is_disklabel(cxt, SGI) ||
848 fdisk_is_disklabel(cxt, GPT))
67987b47 849 print_buffer(cxt, cxt->firstsector);
e3661531
KZ
850
851 else for (i = 3; i < cxt->label->nparts_max; i++)
e53ced85 852 print_buffer(cxt, ptes[i].sectorbuffer);
6dbe3af9
KZ
853}
854
823f0fd1 855static void __attribute__ ((__noreturn__)) handle_quit(struct fdisk_context *cxt)
365f01fc 856{
823f0fd1 857 fdisk_free_context(cxt);
365f01fc
DB
858 printf("\n");
859 exit(EXIT_SUCCESS);
860}
861
22853e4a 862static void
7737f698 863expert_command_prompt(struct fdisk_context *cxt)
e97a991a 864{
22853e4a
KZ
865 char c;
866
e3661531
KZ
867 assert(cxt);
868
6dbe3af9 869 while(1) {
e3661531
KZ
870 assert(cxt->label);
871
6dbe3af9 872 putchar('\n');
bddd84e7 873 c = tolower(read_char(cxt, _("Expert command (m for help): ")));
22853e4a 874 switch (c) {
2b6fc908 875 case 'a':
ff5775bd 876 if (fdisk_is_disklabel(cxt, SUN))
e53ced85 877 sun_set_alt_cyl(cxt);
2b6fc908 878 break;
2b6fc908 879 case 'b':
ff5775bd 880 if (fdisk_is_disklabel(cxt, DOS))
e3661531
KZ
881 dos_move_begin(cxt, get_partition(cxt, 0,
882 cxt->label->nparts_max));
2b6fc908
KZ
883 break;
884 case 'c':
cf3808e4 885 user_cylinders = read_int(cxt, 1, cxt->geom.cylinders, 1048576, 0,
22853e4a 886 _("Number of cylinders"));
aa42788d 887 fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors);
ff5775bd 888 if (fdisk_is_disklabel(cxt, SUN))
24cd580b 889 sun_set_ncyl(cxt, cxt->geom.cylinders);
2b6fc908
KZ
890 break;
891 case 'd':
e53ced85 892 print_raw(cxt);
2b6fc908
KZ
893 break;
894 case 'e':
ff5775bd 895 if (fdisk_is_disklabel(cxt, SGI))
5c36a0eb 896 sgi_set_xcyl();
ff5775bd 897 else if (fdisk_is_disklabel(cxt, SUN))
e53ced85 898 sun_set_xcyl(cxt);
a2c5f3ca 899 else
ff5775bd 900 if (fdisk_is_disklabel(cxt, DOS))
5dfca634 901 dos_list_table_expert(cxt, 1);
5c36a0eb 902 break;
22853e4a 903 case 'f':
ff5775bd 904 if (fdisk_is_disklabel(cxt, DOS))
e3661531 905 dos_fix_partition_table_order(cxt);
22853e4a 906 break;
5c36a0eb 907 case 'g':
de3d6c93 908 fdisk_create_disklabel(cxt, "sgi");
2b6fc908
KZ
909 break;
910 case 'h':
cf3808e4 911 user_heads = read_int(cxt, 1, cxt->geom.heads, 256, 0,
7eda085c 912 _("Number of heads"));
aa42788d 913 fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors);
2b6fc908 914 break;
2b6fc908 915 case 'i':
ff5775bd 916 if (fdisk_is_disklabel(cxt, SUN))
e53ced85 917 sun_set_ilfact(cxt);
ff5775bd 918 else if (fdisk_is_disklabel(cxt, DOS))
38b36353 919 dos_set_mbr_id(cxt);
2b6fc908
KZ
920 break;
921 case 'o':
ff5775bd 922 if (fdisk_is_disklabel(cxt, SUN))
e53ced85 923 sun_set_rspeed(cxt);
2b6fc908 924 break;
2b6fc908 925 case 'p':
ff5775bd 926 if (fdisk_is_disklabel(cxt, SUN))
7737f698 927 list_table(cxt, 1);
2b6fc908 928 else
5dfca634 929 dos_list_table_expert(cxt, 0);
2b6fc908
KZ
930 break;
931 case 'q':
823f0fd1 932 handle_quit(cxt);
2b6fc908
KZ
933 case 'r':
934 return;
935 case 's':
cf3808e4 936 user_sectors = read_int(cxt, 1, cxt->geom.sectors, 63, 0,
7eda085c 937 _("Number of sectors"));
852ce62b 938 if (is_dos_compatible(cxt))
7eda085c 939 fprintf(stderr, _("Warning: setting "
2b6fc908 940 "sector offset for DOS "
6a95c736 941 "compatibility\n"));
aa42788d 942 fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors);
2b6fc908
KZ
943 break;
944 case 'v':
e53ced85 945 verify(cxt);
2b6fc908
KZ
946 break;
947 case 'w':
d8de0955 948 write_table(cxt);
2b6fc908 949 break;
2b6fc908 950 case 'y':
ff5775bd 951 if (fdisk_is_disklabel(cxt, SUN))
e53ced85 952 sun_set_pcylcount(cxt);
2b6fc908 953 break;
2b6fc908 954 default:
ff5775bd 955 print_menu(cxt, EXPERT_MENU);
6dbe3af9
KZ
956 }
957 }
958}
959
3b622ddd
DB
960static int is_ide_cdrom_or_tape(char *device)
961{
962 int fd, ret;
2b6fc908 963
1c6b3378 964 if ((fd = open(device, O_RDONLY)) < 0)
e8f26419 965 return 0;
3b622ddd 966 ret = blkdev_is_cdrom(fd);
2b6fc908 967
3b622ddd
DB
968 close(fd);
969 return ret;
2b6fc908
KZ
970}
971
ea4824f1 972/* Print disk geometry and partition table of a specified device (-l option) */
4e0e8253
KZ
973static void print_partition_table_from_option(struct fdisk_context *cxt,
974 char *device, unsigned long sector_size)
e27b42d5 975{
4e0e8253 976 if (fdisk_context_assign_device(cxt, device, 1) != 0) /* read-only */
289dcc90 977 err(EXIT_FAILURE, _("cannot open %s"), device);
823f0fd1 978
759d093f 979 if (sector_size) /* passed -b option, override autodiscovery */
aa42788d 980 fdisk_override_sector_size(cxt, sector_size);
759d093f 981
9a5e29e9 982 if (user_cylinders || user_heads || user_sectors)
aa42788d 983 fdisk_override_geometry(cxt, user_cylinders,
759d093f 984 user_heads, user_sectors);
84e18f76
KZ
985
986 if (!fdisk_dev_has_disklabel(cxt)) {
987 /*
988 * Try BSD -- TODO: move to list_table() too
989 */
7737f698 990 list_disk_geometry(cxt);
ff5775bd
DB
991 if (!fdisk_is_disklabel(cxt, AIX) &&
992 !fdisk_is_disklabel(cxt, MAC))
7737f698 993 btrydev(cxt);
ff5775bd 994 } else
7737f698 995 list_table(cxt, 0);
6dbe3af9
KZ
996}
997
c129767e
KZ
998/*
999 * for fdisk -l:
1000 * try all things in /proc/partitions that look like a full disk
1001 */
22853e4a 1002static void
4e0e8253
KZ
1003print_all_partition_table_from_option(struct fdisk_context *cxt,
1004 unsigned long sector_size)
ea4824f1 1005{
eb63b9b8 1006 FILE *procpt;
657d9adb 1007 char line[128 + 1], ptname[128 + 1], devname[256];
981b80b7
KZ
1008 int ma, mi;
1009 unsigned long long sz;
eb63b9b8 1010
6bec8710 1011 procpt = fopen(_PATH_PROC_PARTITIONS, "r");
eb63b9b8 1012 if (procpt == NULL) {
6bec8710 1013 fprintf(stderr, _("cannot open %s\n"), _PATH_PROC_PARTITIONS);
eb63b9b8
KZ
1014 return;
1015 }
1016
1017 while (fgets(line, sizeof(line), procpt)) {
bb662090 1018 if (sscanf (line, " %d %d %llu %128[^\n ]",
eb63b9b8
KZ
1019 &ma, &mi, &sz, ptname) != 4)
1020 continue;
c07ebfa1 1021 snprintf(devname, sizeof(devname), "/dev/%s", ptname);
6bec8710
KZ
1022 if (is_whole_disk(devname)) {
1023 char *cn = canonicalize_path(devname);
1024 if (cn) {
0c48d371 1025 if (!is_ide_cdrom_or_tape(cn))
4e0e8253 1026 print_partition_table_from_option(cxt, cn, sector_size);
6bec8710
KZ
1027 free(cn);
1028 }
1029 }
eb63b9b8 1030 }
e8f26419 1031 fclose(procpt);
eb63b9b8
KZ
1032}
1033
22853e4a
KZ
1034static void
1035unknown_command(int c) {
1036 printf(_("%c: unknown command\n"), c);
7eda085c
KZ
1037}
1038
89fd812f
DB
1039static void print_welcome(void)
1040{
1041 printf(_("Welcome to fdisk (%s).\n\n"
1042 "Changes will remain in memory only, until you decide to write them.\n"
1043 "Be careful before using the write command.\n\n"), PACKAGE_STRING);
1044
1045 fflush(stdout);
1046}
1047
7737f698 1048static void command_prompt(struct fdisk_context *cxt)
56c07b96 1049{
4a96a62a 1050 int c;
5dbff4c0 1051
e3661531
KZ
1052 assert(cxt);
1053
ff5775bd 1054 if (fdisk_is_disklabel(cxt, OSF)) {
56c07b96
FC
1055 putchar('\n');
1056 /* OSF label, and no DOS label */
1057 printf(_("Detected an OSF/1 disklabel on %s, entering "
1058 "disklabel mode.\n"),
823f0fd1 1059 cxt->dev_path);
7737f698 1060 bsd_command_prompt(cxt);
53b422ab 1061
56c07b96 1062 /* If we return we may want to make an empty DOS label? */
53b422ab 1063 fdisk_context_switch_label(cxt, "dos");
56c07b96
FC
1064 }
1065
1066 while (1) {
e3661531
KZ
1067
1068 assert(cxt->label);
1069
56c07b96 1070 putchar('\n');
bddd84e7 1071 c = tolower(read_char(cxt, _("Command (m for help): ")));
56c07b96
FC
1072 switch (c) {
1073 case 'a':
ff5775bd 1074 if (fdisk_is_disklabel(cxt, DOS))
e3661531
KZ
1075 dos_toggle_active(cxt,
1076 get_partition(cxt, 1, cxt->label->nparts_max));
ff5775bd 1077 else if (fdisk_is_disklabel(cxt, SUN))
e3661531
KZ
1078 toggle_sunflags(cxt,
1079 get_partition(cxt, 1, cxt->label->nparts_max),
1080 SUN_FLAG_UNMNT);
ff5775bd 1081 else if (fdisk_is_disklabel(cxt, SGI))
38b36353 1082 sgi_set_bootpartition(cxt,
e3661531 1083 get_partition(cxt, 1, cxt->label->nparts_max));
56c07b96
FC
1084 else
1085 unknown_command(c);
1086 break;
1087 case 'b':
53b422ab
KZ
1088 /*
1089 * TODO: create child context for nexted partition tables
1090 */
ff5775bd 1091 if (fdisk_is_disklabel(cxt, SGI))
38b36353 1092 sgi_set_bootfile(cxt);
ff5775bd 1093 else if (fdisk_is_disklabel(cxt, DOS)) {
53b422ab 1094 fdisk_context_switch_label(cxt, "bsd");
7737f698 1095 bsd_command_prompt(cxt);
53b422ab 1096 fdisk_context_switch_label(cxt, "dos");
c482303e
FC
1097 } else
1098 unknown_command(c);
56c07b96
FC
1099 break;
1100 case 'c':
ff5775bd 1101 if (fdisk_is_disklabel(cxt, DOS))
e53ced85 1102 toggle_dos_compatibility_flag(cxt);
ff5775bd 1103 else if (fdisk_is_disklabel(cxt, SUN))
e3661531
KZ
1104 toggle_sunflags(cxt,
1105 get_partition(cxt, 1, cxt->label->nparts_max),
1106 SUN_FLAG_RONLY);
ff5775bd 1107 else if (fdisk_is_disklabel(cxt, SGI))
38b36353 1108 sgi_set_swappartition(cxt,
e3661531 1109 get_partition(cxt, 1, cxt->label->nparts_max));
56c07b96
FC
1110 else
1111 unknown_command(c);
1112 break;
1113 case 'd':
e3661531
KZ
1114 delete_partition(cxt,
1115 get_existing_partition(cxt, 1, cxt->label->nparts_max));
56c07b96 1116 break;
3f731001
DB
1117 case 'g':
1118 fdisk_create_disklabel(cxt, "gpt");
1119 break;
7375423c
KZ
1120 case 'G':
1121 fdisk_create_disklabel(cxt, "sgi");
1122 break;
56c07b96 1123 case 'i':
ff5775bd 1124 if (fdisk_is_disklabel(cxt, SGI))
38b36353 1125 create_sgiinfo(cxt);
56c07b96
FC
1126 else
1127 unknown_command(c);
099c7a94 1128 break;
56c07b96 1129 case 'l':
7b575fcc 1130 list_partition_types(cxt);
56c07b96
FC
1131 break;
1132 case 'm':
ff5775bd 1133 print_menu(cxt, MAIN_MENU);
56c07b96
FC
1134 break;
1135 case 'n':
e53ced85 1136 new_partition(cxt);
56c07b96
FC
1137 break;
1138 case 'o':
de3d6c93 1139 fdisk_create_disklabel(cxt, "dos");
56c07b96
FC
1140 break;
1141 case 'p':
7737f698 1142 list_table(cxt, 0);
56c07b96
FC
1143 break;
1144 case 'q':
823f0fd1 1145 handle_quit(cxt);
56c07b96 1146 case 's':
de3d6c93 1147 fdisk_create_disklabel(cxt, "sun");
56c07b96
FC
1148 break;
1149 case 't':
02460b8a 1150 change_partition_type(cxt);
56c07b96
FC
1151 break;
1152 case 'u':
ec10aa67 1153 toggle_units(cxt);
56c07b96
FC
1154 break;
1155 case 'v':
e53ced85 1156 verify(cxt);
56c07b96
FC
1157 break;
1158 case 'w':
d8de0955 1159 write_table(cxt);
56c07b96
FC
1160 break;
1161 case 'x':
7737f698 1162 expert_command_prompt(cxt);
56c07b96
FC
1163 break;
1164 default:
1165 unknown_command(c);
ff5775bd 1166 print_menu(cxt, MAIN_MENU);
56c07b96
FC
1167 }
1168 }
1169}
5dbff4c0 1170
50dec1eb 1171static sector_t get_dev_blocks(char *dev)
6da365de
DB
1172{
1173 int fd;
50dec1eb 1174 sector_t size;
6da365de
DB
1175
1176 if ((fd = open(dev, O_RDONLY)) < 0)
289dcc90 1177 err(EXIT_FAILURE, _("cannot open %s"), dev);
7737f698
DB
1178 if (blkdev_get_sectors(fd, &size) == -1) {
1179 close(fd);
1180 err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
1181 }
6da365de
DB
1182 close(fd);
1183 return size/2;
1184}
1185
e1144767
DB
1186int main(int argc, char **argv)
1187{
6da365de 1188 int c, optl = 0, opts = 0;
e53ced85 1189 unsigned long sector_size = 0;
4e0e8253 1190 struct fdisk_context *cxt;
852ce62b 1191 struct fdisk_label *lb;
2b6fc908 1192
7eda085c
KZ
1193 setlocale(LC_ALL, "");
1194 bindtextdomain(PACKAGE, LOCALEDIR);
1195 textdomain(PACKAGE);
b2d28533 1196 atexit(close_stdout);
2b6fc908 1197
4e0e8253
KZ
1198 fdisk_init_debug(0);
1199 cxt = fdisk_new_context();
1200 if (!cxt)
1201 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
1202
416c43a9
KZ
1203 fdisk_context_set_ask(cxt, ask_callback, NULL);
1204
278f63c0 1205 while ((c = getopt(argc, argv, "b:c::C:hH:lsS:u::vV")) != -1) {
2b6fc908
KZ
1206 switch (c) {
1207 case 'b':
0e6f4a20
KZ
1208 /* Ugly: this sector size is really per device,
1209 so cannot be combined with multiple disks,
1210 and te same goes for the C/H/S options.
1211 */
00695059 1212 sector_size = strtou32_or_err(optarg, _("invalid sector size argument"));
5c36a0eb 1213 if (sector_size != 512 && sector_size != 1024 &&
8905beda 1214 sector_size != 2048 && sector_size != 4096)
7c1db6b4 1215 usage(stderr);
2b6fc908 1216 break;
0e6f4a20 1217 case 'C':
00695059 1218 user_cylinders = strtou32_or_err(optarg, _("invalid cylinders argument"));
0e6f4a20 1219 break;
78498b7b 1220 case 'c':
852ce62b
KZ
1221 if (optarg) {
1222 lb = fdisk_context_get_label(cxt, "dos");
1223 if (!lb)
1224 err(EXIT_FAILURE, _("not found DOS label driver"));
1225 if (strcmp(optarg, "=dos") == 0)
1226 fdisk_dos_enable_compatible(lb, TRUE);
1227 else if (strcmp(optarg, "=nondos") == 0)
1228 fdisk_dos_enable_compatible(lb, FALSE);
1229 else
1230 usage(stderr);
1231 }
1232 /* use default if no optarg specified */
78498b7b 1233 break;
0e6f4a20 1234 case 'H':
00695059
KZ
1235 user_heads = strtou32_or_err(optarg, _("invalid heads argument"));
1236 if (user_heads > 256)
0e6f4a20
KZ
1237 user_heads = 0;
1238 break;
1239 case 'S':
00695059
KZ
1240 user_sectors = strtou32_or_err(optarg, _("invalid sectors argument"));
1241 if (user_sectors >= 64)
0e6f4a20
KZ
1242 user_sectors = 0;
1243 break;
2b6fc908
KZ
1244 case 'l':
1245 optl = 1;
1246 break;
1247 case 's':
1248 opts = 1;
1249 break;
1250 case 'u':
ec10aa67
KZ
1251 if (optarg && *optarg == '=')
1252 optarg++;
1253 if (fdisk_context_set_unit(cxt, optarg) != 0)
7c1db6b4 1254 usage(stderr);
2b6fc908 1255 break;
22853e4a 1256 case 'V':
2b6fc908 1257 case 'v':
a2482eb3
DB
1258 printf(UTIL_LINUX_VERSION);
1259 return EXIT_SUCCESS;
e1144767
DB
1260 case 'h':
1261 usage(stdout);
2b6fc908 1262 default:
7c1db6b4 1263 usage(stderr);
2b6fc908 1264 }
6dbe3af9 1265 }
2b6fc908 1266
f7b1f75e 1267
14e93ff3 1268 if (sector_size && argc-optind != 1)
7eda085c
KZ
1269 printf(_("Warning: the -b (set sector size) option should"
1270 " be used with one specified device\n"));
7eda085c 1271
2b6fc908 1272 if (optl) {
2b6fc908 1273 nowarn = 1;
2b6fc908
KZ
1274 if (argc > optind) {
1275 int k;
c129767e 1276 for (k = optind; k < argc; k++)
4e0e8253 1277 print_partition_table_from_option(cxt, argv[k], sector_size);
ea4824f1 1278 } else
4e0e8253 1279 print_all_partition_table_from_option(cxt, sector_size);
3e3de41a 1280 exit(EXIT_SUCCESS);
2b6fc908
KZ
1281 }
1282
1283 if (opts) {
6da365de
DB
1284 /* print partition size for one or more devices */
1285 int i, ndevs = argc - optind;
1286 if (ndevs <= 0)
7c1db6b4 1287 usage(stderr);
2b6fc908 1288
6da365de
DB
1289 for (i = optind; i < argc; i++) {
1290 if (ndevs == 1)
1291 printf("%llu\n", get_dev_blocks(argv[i]));
2b6fc908 1292 else
6da365de 1293 printf("%s: %llu\n", argv[i], get_dev_blocks(argv[i]));
6dbe3af9 1294 }
6da365de 1295 exit(EXIT_SUCCESS);
6dbe3af9 1296 }
6dbe3af9 1297
7d2ba340 1298 if (argc-optind != 1)
7c1db6b4 1299 usage(stderr);
7eda085c 1300
4e0e8253 1301 if (fdisk_context_assign_device(cxt, argv[optind], 0) != 0)
289dcc90 1302 err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
759d093f 1303
7d2ba340 1304 if (sector_size) /* passed -b option, override autodiscovery */
aa42788d 1305 fdisk_override_sector_size(cxt, sector_size);
759d093f 1306
9a5e29e9 1307 if (user_cylinders || user_heads || user_sectors)
aa42788d 1308 fdisk_override_geometry(cxt, user_cylinders,
9a5e29e9 1309 user_heads, user_sectors);
7d2ba340 1310
89fd812f 1311 print_welcome();
56c07b96 1312
a71601af
KZ
1313 if (!fdisk_dev_has_disklabel(cxt)) {
1314 fprintf(stderr,
1315 _("Device does not contain a recognized partition table\n"));
639f1d56 1316 fdisk_create_disklabel(cxt, NULL);
a71601af 1317 }
6dbe3af9 1318
7737f698 1319 command_prompt(cxt);
9777759a 1320
4e0e8253 1321 fdisk_free_context(cxt);
5c36a0eb 1322 return 0;
6dbe3af9 1323}