summaryrefslogtreecommitdiffstats
path: root/disk-utils/fdisk.c
diff options
context:
space:
mode:
Diffstat (limited to 'disk-utils/fdisk.c')
-rw-r--r--disk-utils/fdisk.c943
1 files changed, 943 insertions, 0 deletions
diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c
new file mode 100644
index 000000000..e003baf64
--- /dev/null
+++ b/disk-utils/fdisk.c
@@ -0,0 +1,943 @@
+/*
+ * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation: either version 1 or
+ * (at your option) any later version.
+ */
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <limits.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "all-io.h"
+#include "nls.h"
+#include "rpmatch.h"
+#include "blkdev.h"
+#include "mbsalign.h"
+#include "fdisk.h"
+#include "wholedisk.h"
+#include "pathnames.h"
+#include "canonicalize.h"
+#include "strutils.h"
+#include "closestream.h"
+
+#include "pt-sun.h" /* to toggle flags */
+
+#ifdef HAVE_LINUX_COMPILER_H
+# include <linux/compiler.h>
+#endif
+#ifdef HAVE_LINUX_BLKPG_H
+# include <linux/blkpg.h>
+#endif
+
+static void __attribute__ ((__noreturn__)) usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %1$s [options] <disk> change partition table\n"
+ " %1$s [options] -l [<disk>] list partition table(s)\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -b <size> sector size (512, 1024, 2048 or 4096)\n"), out);
+ fputs(_(" -c[=<mode>] compatible mode: 'dos' or 'nondos' (default)\n"), out);
+ fputs(_(" -h print this help text\n"), out);
+ fputs(_(" -c[=<mode>] compatible mode: 'dos' or 'nondos' (default)\n"), out);
+ fputs(_(" -L[=<when>] colorize output (auto, always or never)\n"), out);
+ fputs(_(" -t <type> force fdisk to recognize specified partition table type only\n"), out);
+ fputs(_(" -u[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out);
+ fputs(_(" -v print program version\n"), out);
+ fputs(_(" -C <number> specify the number of cylinders\n"), out);
+ fputs(_(" -H <number> specify the number of heads\n"), out);
+ fputs(_(" -S <number> specify the number of sectors per track\n"), out);
+
+ fprintf(out, USAGE_MAN_TAIL("fdisk(8)"));
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+
+int get_user_reply(struct fdisk_context *cxt, const char *prompt,
+ char *buf, size_t bufsz)
+{
+ char *p;
+ size_t sz;
+
+ do {
+ fputs(prompt, stdout);
+ fflush(stdout);
+
+ if (!fgets(buf, bufsz, stdin)) {
+ if (fdisk_label_is_changed(cxt->label)) {
+ fprintf(stderr, _("\nDo you really want to quit? "));
+
+ if (fgets(buf, bufsz, stdin) && !rpmatch(buf))
+ continue;
+ }
+ fdisk_free_context(cxt);
+ exit(EXIT_FAILURE);
+ } else
+ break;
+ } while (1);
+
+ for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */
+
+ if (p > buf)
+ memmove(buf, p, p - buf); /* remove blank space */
+ sz = strlen(buf);
+ if (sz && *(buf + sz - 1) == '\n')
+ *(buf + sz - 1) = '\0';
+
+ DBG(ASK, dbgprint("user's reply: >>>%s<<<", buf));
+ return 0;
+}
+
+static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask,
+ char *buf, size_t bufsz)
+
+{
+ const char *q = fdisk_ask_get_query(ask);
+ int dft = fdisk_ask_menu_get_default(ask);
+
+ if (q) {
+ fputs(q, stdout); /* print header */
+ fputc('\n', stdout);
+ }
+
+ do {
+ char prompt[128];
+ int key, c;
+ const char *name, *desc;
+ size_t i = 0;
+
+ /* print menu items */
+ while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0)
+ fprintf(stdout, " %c %s (%s)\n", key, name, desc);
+
+ /* ask for key */
+ snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft);
+ get_user_reply(cxt, prompt, buf, bufsz);
+ if (!*buf) {
+ fdisk_info(cxt, _("Using default response %c."), dft);
+ c = dft;
+ } else
+ c = tolower(buf[0]);
+
+ /* check result */
+ i = 0;
+ while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) {
+ if (c == key) {
+ fdisk_ask_menu_set_result(ask, c);
+ return 0; /* success */
+ }
+ }
+ fdisk_warnx(cxt, _("Value out of range."));
+ } while (1);
+
+ return -EINVAL;
+}
+
+
+#define tochar(num) ((int) ('a' + num - 1))
+static int ask_number(struct fdisk_context *cxt,
+ struct fdisk_ask *ask,
+ char *buf, size_t bufsz)
+{
+ char prompt[128] = { '\0' };
+ const char *q = fdisk_ask_get_query(ask);
+ const char *range = fdisk_ask_number_get_range(ask);
+
+ uint64_t dflt = fdisk_ask_number_get_default(ask),
+ low = fdisk_ask_number_get_low(ask),
+ high = fdisk_ask_number_get_high(ask);
+ int inchar = fdisk_ask_number_inchars(ask);
+
+ assert(q);
+
+ DBG(ASK, dbgprint("asking for number "
+ "['%s', <%ju,%ju>, default=%ju, range: %s]",
+ q, low, high, dflt, range));
+
+ if (range && dflt >= low && dflt <= high) {
+ if (inchar)
+ snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "),
+ q, range, tochar(dflt));
+ else
+ snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "),
+ q, range, dflt);
+
+ } else if (dflt >= low && dflt <= high) {
+ if (inchar)
+ snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "),
+ q, tochar(low), tochar(high), tochar(dflt));
+ else
+ snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "),
+ q, low, high, dflt);
+ } else if (inchar)
+ snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "),
+ q, tochar(low), tochar(high));
+ else
+ snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "),
+ q, low, high);
+
+ do {
+ int rc = get_user_reply(cxt, prompt, buf, bufsz);
+ uint64_t num;
+
+ if (rc)
+ return rc;
+ if (!*buf && dflt >= low && dflt <= high)
+ return fdisk_ask_number_set_result(ask, dflt);
+
+ if (isdigit_string(buf)) {
+ char *end;
+
+ errno = 0;
+ num = strtoumax(buf, &end, 10);
+ if (errno || buf == end || (end && *end))
+ continue;
+ } else if (inchar && isalpha(*buf)) {
+ num = tolower(*buf) - 'a' + 1;
+ } else
+ rc = -EINVAL;
+
+ if (rc == 0 && num >= low && num <= high)
+ return fdisk_ask_number_set_result(ask, num);
+
+ fdisk_warnx(cxt, _("Value out of range."));
+ } while (1);
+
+ return -1;
+}
+
+static int ask_offset(struct fdisk_context *cxt,
+ struct fdisk_ask *ask,
+ char *buf, size_t bufsz)
+{
+ char prompt[128] = { '\0' };
+ const char *q = fdisk_ask_get_query(ask);
+ const char *range = fdisk_ask_number_get_range(ask);
+
+ uint64_t dflt = fdisk_ask_number_get_default(ask),
+ low = fdisk_ask_number_get_low(ask),
+ high = fdisk_ask_number_get_high(ask),
+ base = fdisk_ask_number_get_base(ask);
+
+ assert(q);
+
+ DBG(ASK, dbgprint("asking for offset ['%s', <%ju,%ju>, base=%ju, default=%ju, range: %s]",
+ q, low, high, base, dflt, range));
+
+ if (range && dflt >= low && dflt <= high)
+ snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), q, range, dflt);
+ else if (dflt >= low && dflt <= high)
+ snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), q, low, high, dflt);
+ else
+ snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), q, low, high);
+
+ do {
+ uint64_t num = 0;
+ char sig = 0, *p;
+ int pwr = 0;
+
+ int rc = get_user_reply(cxt, prompt, buf, bufsz);
+ if (rc)
+ return rc;
+ if (!*buf && dflt >= low && dflt <= high)
+ return fdisk_ask_number_set_result(ask, dflt);
+
+ p = buf;
+ if (*p == '+' || *p == '-') {
+ sig = *buf;
+ p++;
+ }
+
+ rc = parse_size(p, &num, &pwr);
+ if (rc)
+ continue;
+ DBG(ASK, dbgprint("parsed size: %ju", num));
+ if (sig && pwr) {
+ /* +{size}{K,M,...} specified, the "num" is in bytes */
+ uint64_t unit = fdisk_ask_number_get_unit(ask);
+ num += unit/2; /* round */
+ num /= unit;
+ }
+ if (sig == '+')
+ num += base;
+ else if (sig == '-')
+ num = base - num;
+
+ DBG(ASK, dbgprint("final offset: %ju [sig: %c, power: %d, %s]",
+ num, sig, pwr,
+ sig ? "relative" : "absolute"));
+ if (num >= low && num <= high) {
+ if (sig)
+ fdisk_ask_number_set_relative(ask, 1);
+ return fdisk_ask_number_set_result(ask, num);
+ }
+ fdisk_warnx(cxt, _("Value out of range."));
+ } while (1);
+
+ return -1;
+}
+
+static unsigned int info_count;
+
+static void fputs_info(struct fdisk_ask *ask, FILE *out, char *buf, size_t bufsz)
+{
+ const char *msg;
+ unsigned int flags;
+
+ assert(ask);
+
+ msg = fdisk_ask_print_get_mesg(ask);
+ flags = fdisk_ask_get_flags(ask);
+
+ if (!msg)
+ return;
+ if (info_count == 1)
+ fputc('\n', out);
+ if (flags == 0 || !colors_wanted())
+ goto simple;
+
+ if (flags & FDISK_INFO_COLON) {
+ size_t sz;
+ char *sep = _(": ");
+ char *p = strstr(msg, sep);
+
+ if (!p)
+ goto simple;
+
+ sz = strlen(sep);
+ strncpy(buf, msg, bufsz);
+ buf[p - msg + sz] = '\0';
+
+ color_enable(UL_COLOR_BROWN);
+ fputs(buf, out);
+ color_disable();
+ fputs(p + sz, out);
+
+ } else if (flags & FDISK_INFO_SUCCESS) {
+ color_enable(UL_COLOR_BOLD);
+ fputs(msg, out);
+ color_disable();
+ } else {
+simple:
+ fputs(msg, out);
+ }
+ fputc('\n', out);
+}
+
+int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
+ void *data __attribute__((__unused__)))
+{
+ int rc = 0;
+ char buf[BUFSIZ];
+
+ assert(cxt);
+ assert(ask);
+
+ if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO)
+ info_count = 0;
+
+ switch(fdisk_ask_get_type(ask)) {
+ case FDISK_ASKTYPE_MENU:
+ return ask_menu(cxt, ask, buf, sizeof(buf));
+ case FDISK_ASKTYPE_NUMBER:
+ return ask_number(cxt, ask, buf, sizeof(buf));
+ case FDISK_ASKTYPE_OFFSET:
+ return ask_offset(cxt, ask, buf, sizeof(buf));
+ case FDISK_ASKTYPE_INFO:
+ info_count++;
+ fputs_info(ask, stdout, buf, sizeof(buf));
+ break;
+ case FDISK_ASKTYPE_WARNX:
+ color_fenable(UL_COLOR_RED, stderr);
+ fputs(fdisk_ask_print_get_mesg(ask), stderr);
+ color_fdisable(stderr);
+ fputc('\n', stderr);
+ break;
+ case FDISK_ASKTYPE_WARN:
+ color_fenable(UL_COLOR_RED, stderr);
+ fputs(fdisk_ask_print_get_mesg(ask), stderr);
+ errno = fdisk_ask_print_get_errno(ask);
+ fprintf(stderr, ": %m\n");
+ color_fdisable(stderr);
+ break;
+ case FDISK_ASKTYPE_YESNO:
+ fputc('\n', stdout);
+ fputs(fdisk_ask_get_query(ask), stdout);
+ rc = get_user_reply(cxt, _(" [Y]es/[N]o: "), buf, sizeof(buf));
+ if (rc == 0)
+ fdisk_ask_yesno_set_result(ask, rpmatch(buf));
+ DBG(ASK, dbgprint("yes-no ask: reply '%s' [rc=%d]", buf, rc));
+ break;
+ case FDISK_ASKTYPE_STRING:
+ {
+ char prmt[BUFSIZ];
+ snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask));
+ fputc('\n', stdout);
+ rc = get_user_reply(cxt, prmt, buf, sizeof(buf));
+ if (rc == 0)
+ fdisk_ask_string_set_result(ask, xstrdup(buf));
+ DBG(ASK, dbgprint("string ask: reply '%s' [rc=%d]", buf, rc));
+ break;
+ }
+ default:
+ warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
+ return -EINVAL;
+ }
+ return rc;
+}
+
+struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt)
+{
+ const char *q;
+
+ if (!cxt || !cxt->label || !cxt->label->nparttypes)
+ return NULL;
+
+ q = fdisk_is_parttype_string(cxt) ?
+ _("Partition type (type L to list all types): ") :
+ _("Hex code (type L to list all codes): ");
+ do {
+ char buf[256];
+ int rc = get_user_reply(cxt, q, buf, sizeof(buf));
+
+ if (rc)
+ break;
+
+ if (buf[1] == '\0' && toupper(*buf) == 'L')
+ list_partition_types(cxt);
+ else if (*buf)
+ return fdisk_parse_parttype(cxt, buf);
+ } while (1);
+
+ return NULL;
+}
+
+void list_partition_types(struct fdisk_context *cxt)
+{
+ struct fdisk_parttype *types;
+ size_t ntypes = 0;
+
+ if (!cxt || !cxt->label || !cxt->label->parttypes)
+ return;
+
+ types = cxt->label->parttypes;
+ ntypes = cxt->label->nparttypes;
+
+ if (types[0].typestr == NULL) {
+ /*
+ * Prints in 4 columns in format <hex> <name>
+ */
+ size_t last[4], done = 0, next = 0, size;
+ int i;
+
+ size = ntypes;
+ if (types[ntypes - 1].name == NULL)
+ size--;
+
+ for (i = 3; i >= 0; i--)
+ last[3 - i] = done += (size + i - done) / (i + 1);
+ i = done = 0;
+
+ do {
+ #define NAME_WIDTH 15
+ char name[NAME_WIDTH * MB_LEN_MAX];
+ size_t width = NAME_WIDTH;
+ struct fdisk_parttype *t = &types[next];
+ size_t ret;
+
+ if (t->name) {
+ printf("%c%2x ", i ? ' ' : '\n', t->type);
+ ret = mbsalign(_(t->name), name, sizeof(name),
+ &width, MBS_ALIGN_LEFT, 0);
+
+ if (ret == (size_t)-1 || ret >= sizeof(name))
+ printf("%-15.15s", _(t->name));
+ else
+ fputs(name, stdout);
+ }
+
+ next = last[i++] + done;
+ if (i > 3 || next >= last[i]) {
+ i = 0;
+ next = ++done;
+ }
+ } while (done < last[0]);
+
+ } else {
+ /*
+ * Prints 1 column in format <idx> <name> <typestr>
+ */
+ struct fdisk_parttype *t;
+ size_t i;
+
+ for (i = 0, t = types; t && i < ntypes; t++, i++) {
+ if (t->name)
+ printf("%3zu %-30s %s\n", i + 1,
+ t->name, t->typestr);
+ }
+ }
+ putchar('\n');
+}
+
+void toggle_dos_compatibility_flag(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos");
+ int flag;
+
+ if (!lb)
+ return;
+
+ flag = !fdisk_dos_is_compatible(lb);
+ fdisk_info(cxt, flag ?
+ _("DOS Compatibility flag is set (DEPRECATED!)") :
+ _("DOS Compatibility flag is not set"));
+
+ fdisk_dos_enable_compatible(lb, flag);
+
+ if (fdisk_is_disklabel(cxt, DOS))
+ fdisk_reset_alignment(cxt); /* reset the current label */
+}
+
+void change_partition_type(struct fdisk_context *cxt)
+{
+ size_t i;
+ struct fdisk_parttype *t = NULL;
+ struct fdisk_partition *pa = NULL;
+ const char *old = NULL;
+
+ assert(cxt);
+ assert(cxt->label);
+
+ if (fdisk_ask_partnum(cxt, &i, FALSE))
+ return;
+
+ if (fdisk_get_partition(cxt, i, &pa)) {
+ fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1);
+ return;
+ }
+
+ t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
+ old = t ? t->name : _("Unknown");
+
+ do {
+ t = ask_partition_type(cxt);
+ } while (!t);
+
+ if (fdisk_set_partition_type(cxt, i, t) == 0)
+ fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
+ _("Changed type of partition '%s' to '%s'."),
+ old, t ? t->name : _("Unknown"));
+ else
+ fdisk_info(cxt,
+ _("Type of partition %zu is unchanged: %s."),
+ i + 1, old);
+
+ fdisk_unref_partition(pa);
+}
+
+void list_disk_geometry(struct fdisk_context *cxt)
+{
+ char *id = NULL;
+ uint64_t bytes = cxt->total_sectors * cxt->sector_size;
+ char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE
+ | SIZE_SUFFIX_3LETTER, bytes);
+
+ fdisk_colon(cxt, _("Disk %s: %s, %ju bytes, %ju sectors"),
+ cxt->dev_path, strsz,
+ bytes, (uintmax_t) cxt->total_sectors);
+ free(strsz);
+
+ if (fdisk_require_geometry(cxt) || fdisk_context_use_cylinders(cxt))
+ fdisk_colon(cxt, _("Geometry: %d heads, %llu sectors/track, %llu cylinders"),
+ cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders);
+
+ fdisk_colon(cxt, _("Units: %s of %d * %ld = %ld bytes"),
+ fdisk_context_get_unit(cxt, PLURAL),
+ fdisk_context_get_units_per_sector(cxt),
+ cxt->sector_size,
+ fdisk_context_get_units_per_sector(cxt) * cxt->sector_size);
+
+ fdisk_colon(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"),
+ cxt->sector_size, cxt->phy_sector_size);
+ fdisk_colon(cxt, _("I/O size (minimum/optimal): %lu bytes / %lu bytes"),
+ cxt->min_io_size, cxt->io_size);
+ if (cxt->alignment_offset)
+ fdisk_colon(cxt, _("Alignment offset: %lu bytes"),
+ cxt->alignment_offset);
+ if (fdisk_dev_has_disklabel(cxt))
+ fdisk_colon(cxt, _("Disklabel type: %s"), cxt->label->name);
+
+ if (fdisk_get_disklabel_id(cxt, &id) == 0 && id)
+ fdisk_colon(cxt, _("Disk identifier: %s"), id);
+}
+
+void list_disklabel(struct fdisk_context *cxt)
+{
+ struct fdisk_table *tb = NULL;
+ char *str;
+
+ /* print label specific stuff by libfdisk FDISK_ASK_INFO API */
+ fdisk_list_disklabel(cxt);
+
+ /* print partitions */
+ if (fdisk_get_partitions(cxt, &tb))
+ return;
+ if (fdisk_table_to_string(tb, cxt, NULL, 0, &str) == 0) {
+ fputc('\n', stdout);
+ if (str && *str)
+ fputs(str, stdout);
+ }
+ fdisk_unref_table(tb);
+}
+
+static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz)
+{
+ size_t next;
+ const unsigned char *p0 = buf + i;
+
+ for (next = i + 16; next < sz; next += 16) {
+ if (memcmp(p0, buf + next, 16) != 0)
+ break;
+ }
+
+ return next == i + 16 ? i : next;
+}
+
+static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all)
+{
+ size_t i, l, next = 0;
+
+ if (!buf)
+ return;
+ for (i = 0, l = 0; i < sz; i++, l++) {
+ if (l == 0) {
+ if (all == 0 && !next)
+ next = skip_empty(buf, i, sz);
+ printf("%08jx ", base + i);
+ }
+ printf(" %02x", buf[i]);
+ if (l == 7) /* words separator */
+ fputs(" ", stdout);
+ else if (l == 15) {
+ fputc('\n', stdout); /* next line */
+ l = -1;
+ if (next > i) {
+ printf("*\n");
+ i = next - 1;
+ }
+ next = 0;
+ }
+ }
+ if (l > 0)
+ printf("\n");
+}
+
+static void dump_blkdev(struct fdisk_context *cxt, const char *name,
+ off_t offset, size_t size, int all)
+{
+ fdisk_colon(cxt, _("\n%s: offset = %ju, size = %zu bytes."),
+ name, offset, size);
+
+ if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
+ fdisk_warn(cxt, _("cannot seek"));
+ else {
+ unsigned char *buf = xmalloc(size);
+
+ if (read_all(cxt->dev_fd, (char *) buf, size) != (ssize_t) size)
+ fdisk_warn(cxt, _("cannot read"));
+ else
+ dump_buffer(offset, buf, size, all);
+ free(buf);
+ }
+}
+
+void dump_firstsector(struct fdisk_context *cxt)
+{
+ int all = !isatty(STDOUT_FILENO);
+
+ assert(cxt);
+ assert(cxt->label);
+
+ dump_blkdev(cxt, _("First sector"), 0, cxt->sector_size, all);
+}
+
+void dump_disklabel(struct fdisk_context *cxt)
+{
+ int all = !isatty(STDOUT_FILENO);
+ int i = 0;
+ const char *name = NULL;
+ off_t offset = 0;
+ size_t size = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+
+ while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size)
+ dump_blkdev(cxt, name, offset, size, all);
+}
+
+static int is_ide_cdrom_or_tape(char *device)
+{
+ int fd, ret;
+
+ if ((fd = open(device, O_RDONLY)) < 0)
+ return 0;
+ ret = blkdev_is_cdrom(fd);
+
+ close(fd);
+ return ret;
+}
+
+static void print_device_pt(struct fdisk_context *cxt, char *device)
+{
+ if (fdisk_context_assign_device(cxt, device, 1) != 0) /* read-only */
+ err(EXIT_FAILURE, _("cannot open %s"), device);
+
+ list_disk_geometry(cxt);
+
+ if (fdisk_dev_has_disklabel(cxt))
+ list_disklabel(cxt);
+ fputc('\n', stdout);
+}
+
+static void print_all_devices_pt(struct fdisk_context *cxt)
+{
+ FILE *f;
+ char line[128 + 1];
+
+ f = fopen(_PATH_PROC_PARTITIONS, "r");
+ if (!f) {
+ warn(_("cannot open %s"), _PATH_PROC_PARTITIONS);
+ return;
+ }
+
+ DBG(FRONTEND, dbgprint("reading "_PATH_PROC_PARTITIONS));
+
+ while (fgets(line, sizeof(line), f)) {
+ char ptname[128 + 1], devname[256];
+
+ if (sscanf(line, " %*d %*d %*d %128[^\n ]", ptname) != 1)
+ continue;
+
+ snprintf(devname, sizeof(devname), "/dev/%s", ptname);
+
+ DBG(FRONTEND, dbgprint("listing %s", devname));
+
+ if (is_whole_disk(devname)) {
+ char *cn = canonicalize_path(devname);
+ if (cn) {
+ if (!is_ide_cdrom_or_tape(cn))
+ print_device_pt(cxt, cn);
+ free(cn);
+ }
+ }
+ }
+ fclose(f);
+}
+
+static sector_t get_dev_blocks(char *dev)
+{
+ int fd;
+ sector_t size;
+
+ if ((fd = open(dev, O_RDONLY)) < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), dev);
+ if (blkdev_get_sectors(fd, &size) == -1) {
+ close(fd);
+ err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev);
+ }
+ close(fd);
+ return size/2;
+}
+
+enum {
+ ACT_FDISK = 0, /* default */
+ ACT_LIST,
+ ACT_SHOWSIZE
+};
+
+int main(int argc, char **argv)
+{
+ int i, c, act = ACT_FDISK;
+ int colormode = UL_COLORMODE_UNDEF;
+ struct fdisk_context *cxt;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ fdisk_init_debug(0);
+ cxt = fdisk_new_context();
+ if (!cxt)
+ err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
+
+ fdisk_context_set_ask(cxt, ask_callback, NULL);
+
+ while ((c = getopt(argc, argv, "b:c::C:hH:lL::sS:t:u::vV")) != -1) {
+ switch (c) {
+ case 'b':
+ {
+ size_t sz = strtou32_or_err(optarg,
+ _("invalid sector size argument"));
+ if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096)
+ usage(stderr);
+ fdisk_save_user_sector_size(cxt, sz, sz);
+ break;
+ }
+ case 'C':
+ fdisk_save_user_geometry(cxt,
+ strtou32_or_err(optarg,
+ _("invalid cylinders argument")),
+ 0, 0);
+ break;
+ case 'c':
+ if (optarg) {
+ /* this setting is independent on the current
+ * actively used label */
+ struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos");
+ if (!lb)
+ err(EXIT_FAILURE, _("not found DOS label driver"));
+ if (strcmp(optarg, "=dos") == 0)
+ fdisk_dos_enable_compatible(lb, TRUE);
+ else if (strcmp(optarg, "=nondos") == 0)
+ fdisk_dos_enable_compatible(lb, FALSE);
+ else
+ usage(stderr);
+ }
+ /* use default if no optarg specified */
+ break;
+ case 'H':
+ fdisk_save_user_geometry(cxt, 0,
+ strtou32_or_err(optarg,
+ _("invalid heads argument")),
+ 0);
+ break;
+ case 'S':
+ fdisk_save_user_geometry(cxt, 0, 0,
+ strtou32_or_err(optarg,
+ _("invalid sectors argument")));
+ break;
+ case 'l':
+ act = ACT_LIST;
+ break;
+ case 'L':
+ if (optarg)
+ colormode = colormode_or_err(optarg,
+ _("unsupported color mode"));
+ break;
+ case 's':
+ act = ACT_SHOWSIZE;
+ break;
+ case 't':
+ {
+ struct fdisk_label *lb = NULL;
+
+ while (fdisk_context_next_label(cxt, &lb) == 0)
+ fdisk_label_set_disabled(lb, 1);
+
+ lb = fdisk_context_get_label(cxt, optarg);
+ if (!lb)
+ errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg);
+ fdisk_label_set_disabled(lb, 0);
+ }
+ case 'u':
+ if (optarg && *optarg == '=')
+ optarg++;
+ if (fdisk_context_set_unit(cxt, optarg) != 0)
+ usage(stderr);
+ break;
+ case 'V':
+ case 'v':
+ printf(UTIL_LINUX_VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+ }
+
+ if (argc-optind != 1 && fdisk_has_user_device_properties(cxt))
+ warnx(_("The device properties (sector size and geometry) should"
+ " be used with one specified device only."));
+
+ colors_init(colormode, "fdisk");
+
+ switch (act) {
+ case ACT_LIST:
+ fdisk_context_enable_listonly(cxt, 1);
+
+ if (argc > optind) {
+ int k;
+ for (k = optind; k < argc; k++)
+ print_device_pt(cxt, argv[k]);
+ } else
+ print_all_devices_pt(cxt);
+ break;
+
+ case ACT_SHOWSIZE:
+ /* deprecated */
+ if (argc - optind <= 0)
+ usage(stderr);
+
+ for (i = optind; i < argc; i++) {
+ if (argc - optind == 1)
+ printf("%llu\n", get_dev_blocks(argv[i]));
+ else
+ printf("%s: %llu\n", argv[i], get_dev_blocks(argv[i]));
+ }
+ break;
+
+ case ACT_FDISK:
+ if (argc-optind != 1)
+ usage(stderr);
+
+ /* Here starts interactive mode, use fdisk_{warn,info,..} functions */
+ color_enable(UL_COLOR_GREEN);
+ fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING);
+ color_disable();
+ fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n"
+ "Be careful before using the write command.\n"));
+
+ if (fdisk_context_assign_device(cxt, argv[optind], 0) != 0)
+ err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
+
+ fflush(stdout);
+
+ if (!fdisk_dev_has_disklabel(cxt)) {
+ fdisk_info(cxt, _("Device does not contain a recognized partition table."));
+ fdisk_create_disklabel(cxt, NULL);
+
+ } else if (fdisk_is_disklabel(cxt, GPT) && fdisk_gpt_is_hybrid(cxt))
+ fdisk_warnx(cxt, _(
+ "The hybrid GPT detected. You have to sync "
+ "the hybrid MBR manually (expert command 'M')."));
+
+ while (1)
+ process_fdisk_menu(&cxt);
+ }
+
+ fdisk_free_context(cxt);
+ return EXIT_SUCCESS;
+}