diff options
Diffstat (limited to 'fdisks/fdisk.c')
-rw-r--r-- | fdisks/fdisk.c | 1935 |
1 files changed, 1935 insertions, 0 deletions
diff --git a/fdisks/fdisk.c b/fdisks/fdisk.c new file mode 100644 index 0000000..3a16c5f --- /dev/null +++ b/fdisks/fdisk.c @@ -0,0 +1,1935 @@ +/* fdisk.c -- Partition table manipulator for Linux. + * + * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) + * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org> + * + * 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 "xalloc.h" +#include "nls.h" +#include "rpmatch.h" +#include "blkdev.h" +#include "common.h" +#include "mbsalign.h" +#include "fdisk.h" +#include "wholedisk.h" +#include "pathnames.h" +#include "canonicalize.h" +#include "strutils.h" +#include "closestream.h" + +#include "fdisksunlabel.h" +#include "fdisksgilabel.h" +#include "fdiskaixlabel.h" +#include "fdiskmaclabel.h" +#include "fdiskdoslabel.h" +#include "fdiskbsdlabel.h" + +#ifdef HAVE_LINUX_COMPILER_H +#include <linux/compiler.h> +#endif +#ifdef HAVE_LINUX_BLKPG_H +#include <linux/blkpg.h> +#endif + +#include "gpt.h" + +int MBRbuffer_changed; + +#define hex_val(c) ({ \ + char _c = (c); \ + isdigit(_c) ? _c - '0' : \ + tolower(_c) + 10 - 'a'; \ + }) + + +#define sector(s) ((s) & 0x3f) +#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2)) + +/* menu list description */ + +struct menulist_descr { + char command; /* command key */ + char *description; /* command description */ + enum fdisk_labeltype label[2]; /* disklabel types associated with main and expert menu */ +}; + +static const struct menulist_descr menulist[] = { + {'a', N_("toggle a bootable flag"), {DOS_LABEL, 0}}, + {'a', N_("toggle a read only flag"), {SUN_LABEL, 0}}, + {'a', N_("select bootable partition"), {SGI_LABEL, 0}}, + {'a', N_("change number of alternate cylinders"), {0, SUN_LABEL}}, + {'b', N_("edit bsd disklabel"), {DOS_LABEL, 0}}, + {'b', N_("edit bootfile entry"), {SGI_LABEL, 0}}, + {'b', N_("move beginning of data in a partition"), {0, DOS_LABEL}}, + {'c', N_("toggle the dos compatibility flag"), {DOS_LABEL, 0}}, + {'c', N_("toggle the mountable flag"), {SUN_LABEL, 0}}, + {'c', N_("select sgi swap partition"), {SGI_LABEL, 0}}, + {'c', N_("change number of cylinders"), {0, DOS_LABEL | SUN_LABEL}}, + {'d', N_("delete a partition"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}}, + {'d', N_("print the raw data in the partition table"), {0, ANY_LABEL}}, + {'e', N_("change number of extra sectors per cylinder"), {0, SUN_LABEL}}, + {'e', N_("list extended partitions"), {0, DOS_LABEL}}, + {'e', N_("edit drive data"), {OSF_LABEL, 0}}, + {'f', N_("fix partition order"), {0, DOS_LABEL}}, + {'g', N_("create an IRIX (SGI) partition table"), {0, ANY_LABEL}}, + {'h', N_("change number of heads"), {0, DOS_LABEL | SUN_LABEL}}, + {'i', N_("change interleave factor"), {0, SUN_LABEL}}, + {'i', N_("change the disk identifier"), {0, DOS_LABEL}}, + {'i', N_("install bootstrap"), {OSF_LABEL, 0}}, + {'l', N_("list known partition types"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}}, + {'m', N_("print this menu"), {ANY_LABEL, ANY_LABEL}}, + {'n', N_("add a new partition"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}}, + {'o', N_("create a new empty DOS partition table"), {~OSF_LABEL, 0}}, + {'o', N_("change rotation speed (rpm)"), {0, SUN_LABEL}}, + {'p', N_("print the partition table"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, DOS_LABEL | SUN_LABEL}}, + {'q', N_("quit without saving changes"), {ANY_LABEL, ANY_LABEL}}, + {'r', N_("return to main menu"), {OSF_LABEL, DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL}}, + {'s', N_("create a new empty Sun disklabel"), {~OSF_LABEL, 0}}, + {'s', N_("change number of sectors/track"), {0, DOS_LABEL | SUN_LABEL}}, + {'s', N_("show complete disklabel"), {OSF_LABEL, 0}}, + {'t', N_("change a partition's system id"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}}, + {'u', N_("change display/entry units"), {DOS_LABEL | SUN_LABEL | SGI_LABEL | OSF_LABEL, 0}}, + {'v', N_("verify the partition table"), {DOS_LABEL | SUN_LABEL | SGI_LABEL, DOS_LABEL | SUN_LABEL | SGI_LABEL}}, + {'w', N_("write table to disk and exit"), {DOS_LABEL | SUN_LABEL | SGI_LABEL, DOS_LABEL | SUN_LABEL | SGI_LABEL}}, + {'w', N_("write disklabel to disk"), {OSF_LABEL, 0}}, + {'x', N_("extra functionality (experts only)"), {DOS_LABEL | SUN_LABEL | SGI_LABEL, 0}}, +#if !defined (__alpha__) + {'x', N_("link BSD partition to non-BSD partition"), {OSF_LABEL, 0}}, +#endif + {'y', N_("change number of physical cylinders"), {0, SUN_LABEL}}, +}; + +sector_t get_nr_sects(struct partition *p) { + return read4_little_endian(p->size4); +} + +char *line_ptr, /* interactive input */ + line_buffer[LINE_LENGTH]; + +int nowarn = 0, /* no warnings for fdisk -l/-s */ + dos_compatible_flag = 0, /* disabled by default */ + dos_changed = 0, + partitions = 4; /* maximum partition + 1 */ + +unsigned int user_cylinders, user_heads, user_sectors; +sector_t sector_offset = 1; +unsigned int units_per_sector = 1, display_in_cyl_units = 0; +enum fdisk_labeltype disklabel; /* Current disklabel */ + +static void __attribute__ ((__noreturn__)) usage(FILE *out) +{ + fprintf(out, _("Usage:\n" + " %1$s [options] <disk> change partition table\n" + " %1$s [options] -l <disk> list partition table(s)\n" + " %1$s -s <partition> give partition size(s) in blocks\n" + "\nOptions:\n" + " -b <size> sector size (512, 1024, 2048 or 4096)\n" + " -c[=<mode>] compatible mode: 'dos' or 'nondos' (default)\n" + " -h print this help text\n" + " -u[=<unit>] display units: 'cylinders' or 'sectors' (default)\n" + " -v print program version\n" + " -C <number> specify the number of cylinders\n" + " -H <number> specify the number of heads\n" + " -S <number> specify the number of sectors per track\n" + "\n"), program_invocation_short_name); + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +void __attribute__((__noreturn__)) +fatal(struct fdisk_context *cxt, enum failure why) +{ + close(cxt->dev_fd); + switch (why) { + case unable_to_read: + err(EXIT_FAILURE, _("unable to read %s"), cxt->dev_path); + + case unable_to_seek: + err(EXIT_FAILURE, _("unable to seek on %s"), cxt->dev_path); + + case unable_to_write: + err(EXIT_FAILURE, _("unable to write %s"), cxt->dev_path); + + case ioctl_error: + err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), cxt->dev_path); + + default: + err(EXIT_FAILURE, _("fatal error")); + } +} + +struct partition * +get_part_table(int i) { + return ptes[i].part_table; +} + +void +set_all_unchanged(void) { + int i; + + for (i = 0; i < MAXIMUM_PARTS; i++) + ptes[i].changed = 0; +} + +void +set_changed(int i) { + ptes[i].changed = 1; +} + +static int +is_garbage_table(void) { + int i; + + for (i = 0; i < 4; i++) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + + if (p->boot_ind != 0 && p->boot_ind != 0x80) + return 1; + } + return 0; +} + +void print_menu(enum menutype menu) +{ + size_t i; + + puts(_("Command action")); + + for (i = 0; i < ARRAY_SIZE(menulist); i++) + if (menulist[i].label[menu] & disklabel) + printf(" %c %s\n", menulist[i].command, menulist[i].description); +} + +static int +get_sysid(struct fdisk_context *cxt, int i) { + return ( + disklabel == SUN_LABEL ? sun_get_sysid(cxt, i) : + disklabel == SGI_LABEL ? sgi_get_sysid(cxt, i) : + ptes[i].part_table->sys_ind); +} + +static struct systypes * +get_sys_types(void) { + return ( + disklabel == SUN_LABEL ? sun_sys_types : + disklabel == SGI_LABEL ? sgi_sys_types : + i386_sys_types); +} + +char *partition_type(unsigned char type) +{ + int i; + struct systypes *types = get_sys_types(); + + for (i=0; types[i].name; i++) + if (types[i].type == type) + return _(types[i].name); + + return NULL; +} + +void list_types(struct systypes *sys) +{ + unsigned int last[4], done = 0, next = 0, size; + int i; + + for (i = 0; sys[i].name; i++); + size = i; + + 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; + + printf("%c%2x ", i ? ' ' : '\n', sys[next].type); + size_t ret = mbsalign(_(sys[next].name), name, sizeof(name), + &width, MBS_ALIGN_LEFT, 0); + if (ret == (size_t)-1 || ret >= sizeof(name)) + printf("%-15.15s", _(sys[next].name)); + else + fputs(name, stdout); + + next = last[i++] + done; + if (i > 3 || next >= last[i]) { + i = 0; + next = ++done; + } + } while (done < last[0]); + putchar('\n'); +} + +static int +test_c(char **m, char *mesg) { + int val = 0; + if (!*m) + fprintf(stderr, _("You must set")); + else { + fprintf(stderr, " %s", *m); + val = 1; + } + *m = mesg; + return val; +} + +static int +lba_is_aligned(struct fdisk_context *cxt, sector_t lba) +{ + unsigned int granularity = max(cxt->phy_sector_size, cxt->min_io_size); + unsigned long long offset; + + if (cxt->grain > granularity) + granularity = cxt->grain; + offset = (lba * cxt->sector_size) & (granularity - 1); + + return !((granularity + cxt->alignment_offset - offset) & (granularity - 1)); +} + +static int +lba_is_phy_aligned(struct fdisk_context *cxt, unsigned long long lba) +{ + unsigned int granularity = max(cxt->phy_sector_size, cxt->min_io_size); + unsigned long long offset = (lba * cxt->sector_size) & (granularity - 1); + + return !((granularity + cxt->alignment_offset - offset) & (granularity - 1)); +} + +sector_t align_lba(struct fdisk_context *cxt, sector_t lba, int direction) +{ + sector_t res; + + if (lba_is_aligned(cxt, lba)) + res = lba; + else { + sector_t sects_in_phy = cxt->grain / cxt->sector_size; + + if (lba < sector_offset) + res = sector_offset; + + else if (direction == ALIGN_UP) + res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy; + + else if (direction == ALIGN_DOWN) + res = (lba / sects_in_phy) * sects_in_phy; + + else /* ALIGN_NEAREST */ + res = ((lba + sects_in_phy / 2) / sects_in_phy) * sects_in_phy; + + if (cxt->alignment_offset && !lba_is_aligned(cxt, res) && + res > cxt->alignment_offset / cxt->sector_size) { + /* + * apply alignment_offset + * + * On disk with alignment compensation physical blocks starts + * at LBA < 0 (usually LBA -1). It means we have to move LBA + * according the offset to be on the physical boundary. + */ + /* fprintf(stderr, "LBA: %llu apply alignment_offset\n", res); */ + res -= (max(cxt->phy_sector_size, cxt->min_io_size) - + cxt->alignment_offset) / cxt->sector_size; + + if (direction == ALIGN_UP && res < lba) + res += sects_in_phy; + } + } + + /*** + fprintf(stderr, "LBA %llu (%s) --align-(%s)--> %llu (%s)\n", + lba, + lba_is_aligned(lba) ? "OK" : "FALSE", + direction == ALIGN_UP ? "UP " : + direction == ALIGN_DOWN ? "DOWN " : "NEAREST", + res, + lba_is_aligned(res) ? "OK" : "FALSE"); + ***/ + return res; +} + +int warn_geometry(struct fdisk_context *cxt) +{ + char *m = NULL; + int prev = 0; + + if (disklabel == SGI_LABEL) /* cannot set cylinders etc anyway */ + return 0; + if (!cxt->geom.heads) + prev = test_c(&m, _("heads")); + if (!cxt->geom.sectors) + prev = test_c(&m, _("sectors")); + if (!cxt->geom.cylinders) + prev = test_c(&m, _("cylinders")); + if (!m) + return 0; + fprintf(stderr, + _("%s%s.\nYou can do this from the extra functions menu.\n"), + prev ? _(" and ") : " ", m); + return 1; +} + +void update_units(struct fdisk_context *cxt) +{ + int cyl_units = cxt->geom.heads * cxt->geom.sectors; + + if (display_in_cyl_units && cyl_units) + units_per_sector = cyl_units; + else + units_per_sector = 1; /* in sectors */ +} + +void warn_limits(struct fdisk_context *cxt) +{ + if (cxt->total_sectors > UINT_MAX && !nowarn) { + unsigned long long bytes = cxt->total_sectors * cxt->sector_size; + int giga = bytes / 1000000000; + int hectogiga = (giga + 50) / 100; + + fprintf(stderr, _("\n" +"WARNING: The size of this disk is %d.%d TB (%llu bytes).\n" +"DOS partition table format can not be used on drives for volumes\n" +"larger than (%llu bytes) for %ld-byte sectors. Use parted(1) and GUID \n" +"partition table format (GPT).\n\n"), + hectogiga / 10, hectogiga % 10, + bytes, + (sector_t ) UINT_MAX * cxt->sector_size, + cxt->sector_size); + } +} + +void warn_alignment(struct fdisk_context *cxt) +{ + if (nowarn) + return; + + if (cxt->sector_size != cxt->phy_sector_size) + fprintf(stderr, _("\n" +"The device presents a logical sector size that is smaller than\n" +"the physical sector size. Aligning to a physical sector (or optimal\n" +"I/O) size boundary is recommended, or performance may be impacted.\n")); + + if (dos_compatible_flag) + fprintf(stderr, _("\n" +"WARNING: DOS-compatible mode is deprecated. It's strongly recommended to\n" +" switch off the mode (with command 'c').")); + + if (display_in_cyl_units) + fprintf(stderr, _("\n" +"WARNING: cylinders as display units are deprecated. Use command 'u' to\n" +" change units to sectors.\n")); + +} + +/* + * Sets LBA of the first partition + */ +void +update_sector_offset(struct fdisk_context *cxt) +{ + cxt->grain = cxt->io_size; + + if (dos_compatible_flag) + sector_offset = cxt->geom.sectors; /* usually 63 sectors */ + else { + /* + * Align the begin of partitions to: + * + * a) topology + * a2) alignment offset + * a1) or physical sector (minimal_io_size, aka "grain") + * + * b) or default to 1MiB (2048 sectrors, Windows Vista default) + * + * c) or for very small devices use 1 phy.sector + */ + sector_t x = 0; + + if (fdisk_dev_has_topology(cxt)) { + if (cxt->alignment_offset) + x = cxt->alignment_offset; + else if (cxt->io_size > 2048 * 512) + x = cxt->io_size; + } + /* default to 1MiB */ + if (!x) + x = 2048 * 512; + + sector_offset = x / cxt->sector_size; + + /* don't use huge offset on small devices */ + if (cxt->total_sectors <= sector_offset * 4) + sector_offset = cxt->phy_sector_size / cxt->sector_size; + + /* use 1MiB grain always when possible */ + if (cxt->grain < 2048 * 512) + cxt->grain = 2048 * 512; + + /* don't use huge grain on small devices */ + if (cxt->total_sectors <= (cxt->grain * 4 / cxt->sector_size)) + cxt->grain = cxt->phy_sector_size; + } +} + +static int is_partition_table_changed(void) +{ + int i; + + for (i = 0; i < partitions; i++) + if (ptes[i].changed) + return 1; + return 0; +} + +static void maybe_exit(int rc, int *asked) +{ + char line[LINE_LENGTH]; + + putchar('\n'); + if (asked) + *asked = 0; + + if (is_partition_table_changed() || MBRbuffer_changed) { + fprintf(stderr, _("Do you really want to quit? ")); + + if (!fgets(line, LINE_LENGTH, stdin) || rpmatch(line) == 1) + exit(rc); + if (asked) + *asked = 1; + } else + exit(rc); +} + +/* read line; return 0 or first char */ +int +read_line(int *asked) +{ + line_ptr = line_buffer; + if (!fgets(line_buffer, LINE_LENGTH, stdin)) { + maybe_exit(1, asked); + return 0; + } + if (asked) + *asked = 0; + while (*line_ptr && !isgraph(*line_ptr)) + line_ptr++; + return *line_ptr; +} + +char +read_char(char *mesg) +{ + do { + fputs(mesg, stdout); + fflush (stdout); /* requested by niles@scyld.com */ + } while (!read_line(NULL)); + return *line_ptr; +} + +char +read_chars(char *mesg) +{ + int rc, asked = 0; + + do { + fputs(mesg, stdout); + fflush (stdout); /* niles@scyld.com */ + rc = read_line(&asked); + } while (asked); + + if (!rc) { + *line_ptr = '\n'; + line_ptr[1] = 0; + } + return *line_ptr; +} + +int +read_hex(struct systypes *sys) +{ + int hex; + + while (1) + { + read_char(_("Hex code (type L to list codes): ")); + if (tolower(*line_ptr) == 'l') + list_types(sys); + else if (isxdigit (*line_ptr)) + { + hex = 0; + do + hex = hex << 4 | hex_val(*line_ptr++); + while (isxdigit(*line_ptr)); + return hex; + } + } +} + +unsigned int +read_int_with_suffix(struct fdisk_context *cxt, + unsigned int low, unsigned int dflt, unsigned int high, + unsigned int base, char *mesg, int *is_suffix_used) +{ + unsigned int res; + int default_ok = 1; + int absolute = 0; + static char *ms = NULL; + static size_t mslen = 0; + + if (!ms || strlen(mesg)+100 > mslen) { + mslen = strlen(mesg)+200; + ms = xrealloc(ms,mslen); + } + + if (dflt < low || dflt > high) + default_ok = 0; + + if (default_ok) + snprintf(ms, mslen, _("%s (%u-%u, default %u): "), + mesg, low, high, dflt); + else + snprintf(ms, mslen, "%s (%u-%u): ", + mesg, low, high); + + while (1) { + int use_default = default_ok; + + /* ask question and read answer */ + while (read_chars(ms) != '\n' && !isdigit(*line_ptr) + && *line_ptr != '-' && *line_ptr != '+') + continue; + + if (*line_ptr == '+' || *line_ptr == '-') { + int minus = (*line_ptr == '-'); + int suflen; + + absolute = 0; + res = atoi(line_ptr + 1); + + while (isdigit(*++line_ptr)) + use_default = 0; + + while (isspace(*line_ptr)) + line_ptr++; + + suflen = strlen(line_ptr) - 1; + + while(isspace(*(line_ptr + suflen))) + *(line_ptr + suflen--) = '\0'; + + if ((*line_ptr == 'C' || *line_ptr == 'c') && + *(line_ptr + 1) == '\0') { + /* + * Cylinders + */ + if (!display_in_cyl_units) + res *= cxt->geom.heads * cxt->geom.sectors; + } else if (*line_ptr && + *(line_ptr + 1) == 'B' && + *(line_ptr + 2) == '\0') { + /* + * 10^N + */ + if (*line_ptr == 'K') + absolute = 1000; + else if (*line_ptr == 'M') + absolute = 1000000; + else if (*line_ptr == 'G') + absolute = 1000000000; + else + absolute = -1; + } else if (*line_ptr && + *(line_ptr + 1) == '\0') { + /* + * 2^N + */ + if (*line_ptr == 'K') + absolute = 1 << 10; + else if (*line_ptr == 'M') + absolute = 1 << 20; + else if (*line_ptr == 'G') + absolute = 1 << 30; + else + absolute = -1; + } else if (*line_ptr != '\0') + absolute = -1; + + if (absolute == -1) { + printf(_("Unsupported suffix: '%s'.\n"), line_ptr); + printf(_("Supported: 10^N: KB (KiloByte), MB (MegaByte), GB (GigaByte)\n" + " 2^N: K (KibiByte), M (MebiByte), G (GibiByte)\n")); + continue; + } + + if (absolute && res) { + unsigned long long bytes; + unsigned long unit; + + bytes = (unsigned long long) res * absolute; + unit = cxt->sector_size * units_per_sector; + bytes += unit/2; /* round */ + bytes /= unit; + res = bytes; + } + if (minus) + res = -res; + res += base; + } else { + res = atoi(line_ptr); + while (isdigit(*line_ptr)) { + line_ptr++; + use_default = 0; + } + } + if (use_default) { + printf(_("Using default value %u\n"), dflt); + return dflt; + } + if (res >= low && res <= high) + break; + else + printf(_("Value out of range.\n")); + } + if (is_suffix_used) + *is_suffix_used = absolute > 0; + return res; +} + +/* + * Print the message MESG, then read an integer in LOW..HIGH. + * If the user hits Enter, DFLT is returned, provided that is in LOW..HIGH. + * Answers like +10 are interpreted as offsets from BASE. + * + * There is no default if DFLT is not between LOW and HIGH. + */ +unsigned int +read_int(struct fdisk_context *cxt, + unsigned int low, unsigned int dflt, unsigned int high, + unsigned int base, char *mesg) +{ + return read_int_with_suffix(cxt, low, dflt, high, base, mesg, NULL); +} + +int +get_partition_dflt(struct fdisk_context *cxt, int warn, int max, int dflt) { + struct pte *pe; + int i; + + i = read_int(cxt, 1, dflt, max, 0, _("Partition number")) - 1; + pe = &ptes[i]; + + if (warn) { + if ((disklabel != SUN_LABEL && disklabel != SGI_LABEL && !pe->part_table->sys_ind) + || (disklabel == SUN_LABEL && + (!sunlabel->partitions[i].num_sectors || + !sunlabel->part_tags[i].tag)) + || (disklabel == SGI_LABEL && (!sgi_get_num_sectors(cxt, i))) + ) + fprintf(stderr, + _("Warning: partition %d has empty type\n"), + i+1); + } + return i; +} + +int +get_partition(struct fdisk_context *cxt, int warn, int max) { + return get_partition_dflt(cxt, warn, max, 0); +} + +/* User partition selection unless one partition only is available */ + +static int +get_existing_partition(struct fdisk_context *cxt, int warn, int max) { + int pno = -1; + int i; + + if (disklabel != DOS_LABEL) + goto not_implemented; + + for (i = 0; i < max; i++) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + + if (p && !is_cleared_partition(p)) { + if (pno >= 0) + goto not_unique; + pno = i; + } + } + + if (pno >= 0) { + printf(_("Selected partition %d\n"), pno+1); + return pno; + } + printf(_("No partition is defined yet!\n")); + return -1; + +not_implemented: +not_unique: + return get_partition(cxt, warn, max); +} + +const char * +str_units(int n) +{ + if (display_in_cyl_units) + return P_("cylinder", "cylinders", n); + return P_("sector", "sectors", n); +} + +void change_units(struct fdisk_context *cxt) +{ + display_in_cyl_units = !display_in_cyl_units; + update_units(cxt); + + if (display_in_cyl_units) + printf(_("Changing display/entry units to cylinders (DEPRECATED!)\n")); + else + printf(_("Changing display/entry units to sectors\n")); +} + +static void +toggle_active(int i) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + + if (IS_EXTENDED (p->sys_ind) && !p->boot_ind) + fprintf(stderr, + _("WARNING: Partition %d is an extended partition\n"), + i + 1); + p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG); + pe->changed = 1; +} + +static void +toggle_dos_compatibility_flag(struct fdisk_context *cxt) { + dos_compatible_flag = ~dos_compatible_flag; + if (dos_compatible_flag) + printf(_("DOS Compatibility flag is set (DEPRECATED!)\n")); + else + printf(_("DOS Compatibility flag is not set\n")); + + update_sector_offset(cxt); +} + +static void delete_partition(struct fdisk_context *cxt, int partnum) +{ + if (partnum < 0 || warn_geometry(cxt)) + return; + + ptes[partnum].changed = 1; + fdisk_delete_partition(cxt, partnum); + printf(_("Partition %d is deleted\n"), partnum + 1); +} + +static void change_sysid(struct fdisk_context *cxt) +{ + char *temp; + int i, sys, origsys; + struct partition *p; + + i = get_existing_partition(cxt, 0, partitions); + + if (i == -1) + return; + p = ptes[i].part_table; + origsys = sys = get_sysid(cxt, i); + + /* if changing types T to 0 is allowed, then + the reverse change must be allowed, too */ + if (!sys && disklabel != SGI_LABEL && disklabel != SUN_LABEL && !get_nr_sects(p)) + printf(_("Partition %d does not exist yet!\n"), i + 1); + else while (1) { + sys = read_hex (get_sys_types()); + + if (!sys && disklabel != SGI_LABEL && disklabel != SUN_LABEL) { + printf(_("Type 0 means free space to many systems\n" + "(but not to Linux). Having partitions of\n" + "type 0 is probably unwise. You can delete\n" + "a partition using the `d' command.\n")); + /* break; */ + } + + if (disklabel != SGI_LABEL && disklabel != SUN_LABEL) { + if (IS_EXTENDED (sys) != IS_EXTENDED (p->sys_ind)) { + printf(_("You cannot change a partition into" + " an extended one or vice versa\n" + "Delete it first.\n")); + break; + } + } + + if (sys < 256) { + if (disklabel == SUN_LABEL && i == 2 && sys != SUN_TAG_BACKUP) + printf(_("Consider leaving partition 3 " + "as Whole disk (5),\n" + "as SunOS/Solaris expects it and " + "even Linux likes it.\n\n")); + if (disklabel == SGI_LABEL && ((i == 10 && sys != ENTIRE_DISK) + || (i == 8 && sys != 0))) + printf(_("Consider leaving partition 9 " + "as volume header (0),\nand " + "partition 11 as entire volume (6), " + "as IRIX expects it.\n\n")); + if (sys == origsys) + break; + if (disklabel == SUN_LABEL) { + ptes[i].changed = sun_change_sysid(cxt, i, sys); + } else + if (disklabel == SGI_LABEL) { + ptes[i].changed = sgi_change_sysid(cxt, i, sys); + } else { + p->sys_ind = sys; + ptes[i].changed = 1; + } + temp = partition_type(sys) ? : _("Unknown"); + if (ptes[i].changed) + printf (_("Changed system type of partition %d " + "to %x (%s)\n"), i + 1, sys, temp); + else + printf (_("System type of partition %d is unchanged: " + "%x (%s)\n"), i + 1, sys, temp); + if (is_dos_partition(origsys) || + is_dos_partition(sys)) + dos_changed = 1; + break; + } + } +} + +/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993, + * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross, + * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S. + * Lubkin Oct. 1991). */ + +static void +long2chs(struct fdisk_context *cxt, unsigned long ls, + unsigned int *c, unsigned int *h, unsigned int *s) { + int spc = cxt->geom.heads * cxt->geom.sectors; + + *c = ls / spc; + ls = ls % spc; + *h = ls / cxt->geom.sectors; + *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */ +} + +void check_consistency(struct fdisk_context *cxt, struct partition *p, int partition) +{ + unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */ + unsigned int pec, peh, pes; /* physical ending c, h, s */ + unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */ + unsigned int lec, leh, les; /* logical ending c, h, s */ + + if (!dos_compatible_flag) + return; + + if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4)) + return; /* do not check extended partitions */ + +/* physical beginning c, h, s */ + pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300); + pbh = p->head; + pbs = p->sector & 0x3f; + +/* physical ending c, h, s */ + pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300); + peh = p->end_head; + pes = p->end_sector & 0x3f; + +/* compute logical beginning (c, h, s) */ + long2chs(cxt, get_start_sect(p), &lbc, &lbh, &lbs); + +/* compute logical ending (c, h, s) */ + long2chs(cxt, get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les); + +/* Same physical / logical beginning? */ + if (cxt->geom.cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) { + printf(_("Partition %d has different physical/logical " + "beginnings (non-Linux?):\n"), partition + 1); + printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs); + printf(_("logical=(%d, %d, %d)\n"),lbc, lbh, lbs); + } + +/* Same physical / logical ending? */ + if (cxt->geom.cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) { + printf(_("Partition %d has different physical/logical " + "endings:\n"), partition + 1); + printf(_(" phys=(%d, %d, %d) "), pec, peh, pes); + printf(_("logical=(%d, %d, %d)\n"),lec, leh, les); + } + +/* Ending on cylinder boundary? */ + if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) { + printf(_("Partition %i does not end on cylinder boundary.\n"), + partition + 1); + } +} + +void check_alignment(struct fdisk_context *cxt, sector_t lba, int partition) +{ + if (!lba_is_phy_aligned(cxt, lba)) + printf(_("Partition %i does not start on physical sector boundary.\n"), + partition + 1); +} + +static void +list_disk_geometry(struct fdisk_context *cxt) { + unsigned long long bytes = cxt->total_sectors * cxt->sector_size; + long megabytes = bytes/1000000; + + if (megabytes < 10000) + printf(_("\nDisk %s: %ld MB, %lld bytes"), + cxt->dev_path, megabytes, bytes); + else { + long hectomega = (megabytes + 50) / 100; + printf(_("\nDisk %s: %ld.%ld GB, %llu bytes"), + cxt->dev_path, hectomega / 10, hectomega % 10, bytes); + } + printf(_(", %llu sectors\n"), cxt->total_sectors); + if (dos_compatible_flag) + printf(_("%d heads, %llu sectors/track, %llu cylinders\n"), + cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders); + printf(_("Units = %s of %d * %ld = %ld bytes\n"), + str_units(PLURAL), + units_per_sector, cxt->sector_size, units_per_sector * cxt->sector_size); + + printf(_("Sector size (logical/physical): %lu bytes / %lu bytes\n"), + cxt->sector_size, cxt->phy_sector_size); + printf(_("I/O size (minimum/optimal): %lu bytes / %lu bytes\n"), + cxt->min_io_size, cxt->io_size); + if (cxt->alignment_offset) + printf(_("Alignment offset: %lu bytes\n"), cxt->alignment_offset); + if (disklabel == DOS_LABEL) + dos_print_mbr_id(cxt); + printf("\n"); +} + +/* + * Check whether partition entries are ordered by their starting positions. + * Return 0 if OK. Return i if partition i should have been earlier. + * Two separate checks: primary and logical partitions. + */ +static int +wrong_p_order(int *prev) { + struct pte *pe; + struct partition *p; + unsigned int last_p_start_pos = 0, p_start_pos; + int i, last_i = 0; + + for (i = 0 ; i < partitions; i++) { + if (i == 4) { + last_i = 4; + last_p_start_pos = 0; + } + pe = &ptes[i]; + if ((p = pe->part_table)->sys_ind) { + p_start_pos = get_partition_start(pe); + + if (last_p_start_pos > p_start_pos) { + if (prev) + *prev = last_i; + return i; + } + + last_p_start_pos = p_start_pos; + last_i = i; + } + } + return 0; +} + +/* + * Fix the chain of logicals. + * extended_offset is unchanged, the set of sectors used is unchanged + * The chain is sorted so that sectors increase, and so that + * starting sectors increase. + * + * After this it may still be that cfdisk doesn't like the table. + * (This is because cfdisk considers expanded parts, from link to + * end of partition, and these may still overlap.) + * Now + * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda + * may help. + */ +static void +fix_chain_of_logicals(void) { + int j, oj, ojj, sj, sjj; + struct partition *pj,*pjj,tmp; + + /* Stage 1: sort sectors but leave sector of part 4 */ + /* (Its sector is the global extended_offset.) */ + stage1: + for (j = 5; j < partitions-1; j++) { + oj = ptes[j].offset; + ojj = ptes[j+1].offset; + if (oj > ojj) { + ptes[j].offset = ojj; + ptes[j+1].offset = oj; + pj = ptes[j].part_table; + set_start_sect(pj, get_start_sect(pj)+oj-ojj); + pjj = ptes[j+1].part_table; + set_start_sect(pjj, get_start_sect(pjj)+ojj-oj); + set_start_sect(ptes[j-1].ext_pointer, + ojj-extended_offset); + set_start_sect(ptes[j].ext_pointer, + oj-extended_offset); + goto stage1; + } + } + + /* Stage 2: sort starting sectors */ + stage2: + for (j = 4; j < partitions-1; j++) { + pj = ptes[j].part_table; + pjj = ptes[j+1].part_table; + sj = get_start_sect(pj); + sjj = get_start_sect(pjj); + oj = ptes[j].offset; + ojj = ptes[j+1].offset; + if (oj+sj > ojj+sjj) { + tmp = *pj; + *pj = *pjj; + *pjj = tmp; + set_start_sect(pj, ojj+sjj-oj); + set_start_sect(pjj, oj+sj-ojj); + goto stage2; + } + } + + /* Probably something was changed */ + for (j = 4; j < partitions; j++) + ptes[j].changed = 1; +} + +static void +fix_partition_table_order(void) { + struct pte *pei, *pek; + int i,k; + + if (!wrong_p_order(NULL)) { + printf(_("Nothing to do. Ordering is correct already.\n\n")); + return; + } + + while ((i = wrong_p_order(&k)) != 0 && i < 4) { + /* partition i should have come earlier, move it */ + /* We have to move data in the MBR */ + struct partition *pi, *pk, *pe, pbuf; + pei = &ptes[i]; + pek = &ptes[k]; + + pe = pei->ext_pointer; + pei->ext_pointer = pek->ext_pointer; + pek->ext_pointer = pe; + + pi = pei->part_table; + pk = pek->part_table; + + memmove(&pbuf, pi, sizeof(struct partition)); + memmove(pi, pk, sizeof(struct partition)); + memmove(pk, &pbuf, sizeof(struct partition)); + + pei->changed = pek->changed = 1; + } + + if (i) + fix_chain_of_logicals(); + + printf(_("Done.\n")); + +} + +static void +list_table(struct fdisk_context *cxt, int xtra) { + struct partition *p; + char *type; + int i, w; + + if (disklabel == SUN_LABEL) { + sun_list_table(cxt, xtra); + return; + } + + if (disklabel == SGI_LABEL) { + sgi_list_table(cxt, xtra); + return; + } + + list_disk_geometry(cxt); + + if (disklabel == OSF_LABEL) { + xbsd_print_disklabel(cxt, xtra); + return; + } + + if (is_garbage_table()) { + printf(_("This doesn't look like a partition table\n" + "Probably you selected the wrong device.\n\n")); + } + + /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3, + but if the device name ends in a digit, say /dev/foo1, + then the partition is called /dev/foo1p3. */ + w = strlen(cxt->dev_path); + if (w && isdigit(cxt->dev_path[w-1])) + w++; + if (w < 5) + w = 5; + + printf(_("%*s Boot Start End Blocks Id System\n"), + w+1, _("Device")); + + for (i = 0; i < partitions; i++) { + struct pte *pe = &ptes[i]; + + p = pe->part_table; + if (p && !is_cleared_partition(p)) { + unsigned int psects = get_nr_sects(p); + unsigned int pblocks = psects; + unsigned int podd = 0; + + if (cxt->sector_size < 1024) { + pblocks /= (1024 / cxt->sector_size); + podd = psects % (1024 / cxt->sector_size); + } + if (cxt->sector_size > 1024) + pblocks *= (cxt->sector_size / 1024); + printf( + "%s %c %11lu %11lu %11lu%c %2x %s\n", + partname(cxt->dev_path, i+1, w+2), +/* boot flag */ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG + ? '*' : '?', +/* start */ (unsigned long) cround(get_partition_start(pe)), +/* end */ (unsigned long) cround(get_partition_start(pe) + psects + - (psects ? 1 : 0)), +/* odd flag on end */ (unsigned long) pblocks, podd ? '+' : ' ', +/* type id */ p->sys_ind, +/* type name */ (type = partition_type(p->sys_ind)) ? + type : _("Unknown")); + check_consistency(cxt, p, i); + check_alignment(cxt, get_partition_start(pe), i); + } + } + + /* Is partition table in disk order? It need not be, but... */ + /* partition table entries are not checked for correct order if this + is a sgi, sun or aix labeled disk... */ + if (disklabel == DOS_LABEL && wrong_p_order(NULL)) { + printf(_("\nPartition table entries are not in disk order\n")); + } +} + +static void +x_list_table(struct fdisk_context *cxt, int extend) { + struct pte *pe; + struct partition *p; + int i; + + printf(_("\nDisk %s: %d heads, %llu sectors, %llu cylinders\n\n"), + cxt->dev_path, cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders); + printf(_("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n")); + for (i = 0 ; i < partitions; i++) { + pe = &ptes[i]; + p = (extend ? pe->ext_pointer : pe->part_table); + if (p != NULL) { + printf("%2d %02x%4d%4d%5d%4d%4d%5d%11lu%11lu %02x\n", + i + 1, p->boot_ind, p->head, + sector(p->sector), + cylinder(p->sector, p->cyl), p->end_head, + sector(p->end_sector), + cylinder(p->end_sector, p->end_cyl), + (unsigned long) get_start_sect(p), + (unsigned long) get_nr_sects(p), p->sys_ind); + if (p->sys_ind) { + check_consistency(cxt, p, i); + check_alignment(cxt, get_partition_start(pe), i); + } + } + } +} + +void fill_bounds(sector_t *first, sector_t *last) +{ + int i; + struct pte *pe = &ptes[0]; + struct partition *p; + + for (i = 0; i < partitions; pe++,i++) { + p = pe->part_table; + if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) { + first[i] = 0xffffffff; + last[i] = 0; + } else { + first[i] = get_partition_start(pe); + last[i] = first[i] + get_nr_sects(p) - 1; + } + } +} + +void check(struct fdisk_context *cxt, int n, + unsigned int h, unsigned int s, unsigned int c, + unsigned int start) +{ + unsigned int total, real_s, real_c; + + real_s = sector(s) - 1; + real_c = cylinder(s, c); + total = (real_c * cxt->geom.sectors + real_s) * cxt->geom.heads + h; + if (!total) + fprintf(stderr, _("Warning: partition %d contains sector 0\n"), n); + if (h >= cxt->geom.heads) + fprintf(stderr, + _("Partition %d: head %d greater than maximum %d\n"), + n, h + 1, cxt->geom.heads); + if (real_s >= cxt->geom.sectors) + fprintf(stderr, _("Partition %d: sector %d greater than " + "maximum %llu\n"), n, s, cxt->geom.sectors); + if (real_c >= cxt->geom.cylinders) + fprintf(stderr, _("Partitions %d: cylinder %d greater than " + "maximum %llu\n"), n, real_c + 1, cxt->geom.cylinders); + if (cxt->geom.cylinders <= 1024 && start != total) + fprintf(stderr, + _("Partition %d: previous sectors %d disagrees with " + "total %d\n"), n, start, total); +} + +static void verify(struct fdisk_context *cxt) +{ + if (warn_geometry(cxt)) + return; + + fdisk_verify_disklabel(cxt); +} + +void print_partition_size(struct fdisk_context *cxt, + int num, sector_t start, sector_t stop, int sysid) +{ + char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, + (uint64_t)(stop - start + 1) * cxt->sector_size); + printf(_("Partition %d of type %s and of size %s is set\n"), num, partition_type(sysid), str); + free(str); +} + +static void new_partition(struct fdisk_context *cxt) +{ + int partnum = 0; + + if (warn_geometry(cxt)) + return; + + if (disklabel == SUN_LABEL || disklabel == SGI_LABEL) + partnum = get_partition(cxt, 0, partitions); + + /* + * Use default LINUX_NATIVE partition type, DOS labels + * may override this internally. + */ + fdisk_add_partition(cxt, partnum, LINUX_NATIVE); +} + +static void write_table(struct fdisk_context *cxt) +{ + int rc; + + rc = fdisk_write_disklabel(cxt); + if (rc) + err(EXIT_FAILURE, _("cannot write disk label")); + + printf(_("The partition table has been altered!\n\n")); + reread_partition_table(cxt, 1); +} + +void +reread_partition_table(struct fdisk_context *cxt, int leave) { + int i; + struct stat statbuf; + + i = fstat(cxt->dev_fd, &statbuf); + if (i == 0 && S_ISBLK(statbuf.st_mode)) { + sync(); +#ifdef BLKRRPART + printf(_("Calling ioctl() to re-read partition table.\n")); + i = ioctl(cxt->dev_fd, BLKRRPART); +#else + errno = ENOSYS; + i = 1; +#endif + } + + if (i) { + printf(_("\nWARNING: Re-reading the partition table failed with error %d: %m.\n" + "The kernel still uses the old table. The new table will be used at\n" + "the next reboot or after you run partprobe(8) or kpartx(8)\n"), + errno); + } + + if (dos_changed) + printf( + _("\nWARNING: If you have created or modified any DOS 6.x\n" + "partitions, please see the fdisk manual page for additional\n" + "information.\n")); + + if (leave) { + if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) { + fprintf(stderr, _("\nError closing file\n")); + exit(1); + } + + printf(_("Syncing disks.\n")); + sync(); + exit(!!i); + } +} + +#define MAX_PER_LINE 16 +static void +print_buffer(struct fdisk_context *cxt, unsigned char pbuffer[]) { + unsigned int i, l; + + for (i = 0, l = 0; i < cxt->sector_size; i++, l++) { + if (l == 0) + printf("0x%03X:", i); + printf(" %02X", pbuffer[i]); + if (l == MAX_PER_LINE - 1) { + printf("\n"); + l = -1; + } + } + if (l > 0) + printf("\n"); + printf("\n"); +} + +static void print_raw(struct fdisk_context *cxt) +{ + int i; + + printf(_("Device: %s\n"), cxt->dev_path); + if (disklabel == SUN_LABEL || disklabel == SGI_LABEL) + print_buffer(cxt, cxt->firstsector); + else for (i = 3; i < partitions; i++) + print_buffer(cxt, ptes[i].sectorbuffer); +} + +static void +move_begin(struct fdisk_context *cxt, int i) { + struct pte *pe = &ptes[i]; + struct partition *p = pe->part_table; + unsigned int new, free_start, curr_start, last; + int x; + + if (warn_geometry(cxt)) + return; + if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED (p->sys_ind)) { + printf(_("Partition %d has no data area\n"), i + 1); + return; + } + + /* the default start is at the second sector of the disk or at the + * second sector of the extended partition + */ + free_start = pe->offset ? pe->offset + 1 : 1; + + curr_start = get_partition_start(pe); + + /* look for a free space before the current start of the partition */ + for (x = 0; x < partitions; x++) { + unsigned int end; + struct pte *prev_pe = &ptes[x]; + struct partition *prev_p = prev_pe->part_table; + + if (!prev_p) + continue; + end = get_partition_start(prev_pe) + get_nr_sects(prev_p); + + if (!is_cleared_partition(prev_p) && + end > free_start && end <= curr_start) + free_start = end; + } + + last = get_partition_start(pe) + get_nr_sects(p) - 1; + + new = read_int(cxt, free_start, curr_start, last, free_start, + _("New beginning of data")) - pe->offset; + + if (new != get_nr_sects(p)) { + unsigned int sects = get_nr_sects(p) + get_start_sect(p) - new; + set_nr_sects(p, sects); + set_start_sect(p, new); + pe->changed = 1; + } +} + +static void __attribute__ ((__noreturn__)) handle_quit(struct fdisk_context *cxt) +{ + fdisk_free_context(cxt); + printf("\n"); + exit(EXIT_SUCCESS); +} + +static void +expert_command_prompt(struct fdisk_context *cxt) +{ + char c; + + while(1) { + putchar('\n'); + c = tolower(read_char(_("Expert command (m for help): "))); + switch (c) { + case 'a': + if (disklabel == SUN_LABEL) + sun_set_alt_cyl(cxt); + break; + case 'b': + if (disklabel == DOS_LABEL) + move_begin(cxt, get_partition(cxt, 0, partitions)); + break; + case 'c': + user_cylinders = cxt->geom.cylinders = + read_int(cxt, 1, cxt->geom.cylinders, 1048576, 0, + _("Number of cylinders")); + if (disklabel == SUN_LABEL) + sun_set_ncyl(cxt, cxt->geom.cylinders); + break; + case 'd': + print_raw(cxt); + break; + case 'e': + if (disklabel == SGI_LABEL) + sgi_set_xcyl(); + else if (disklabel == SUN_LABEL) + sun_set_xcyl(cxt); + else + if (disklabel == DOS_LABEL) + x_list_table(cxt, 1); + break; + case 'f': + if (disklabel == DOS_LABEL) + fix_partition_table_order(); + break; + case 'g': + fdisk_create_disklabel(cxt, "sgi"); + break; + case 'h': + user_heads = cxt->geom.heads = read_int(cxt, 1, cxt->geom.heads, 256, 0, + _("Number of heads")); + update_units(cxt); + break; + case 'i': + if (disklabel == SUN_LABEL) + sun_set_ilfact(cxt); + else if (disklabel == DOS_LABEL) + dos_set_mbr_id(cxt); + break; + case 'o': + if (disklabel == SUN_LABEL) + sun_set_rspeed(cxt); + break; + case 'p': + if (disklabel == SUN_LABEL) + list_table(cxt, 1); + else + x_list_table(cxt, 0); + break; + case 'q': + handle_quit(cxt); + case 'r': + return; + case 's': + user_sectors = cxt->geom.sectors = read_int(cxt, 1, cxt->geom.sectors, 63, 0, + _("Number of sectors")); + if (dos_compatible_flag) + fprintf(stderr, _("Warning: setting " + "sector offset for DOS " + "compatibility\n")); + update_sector_offset(cxt); + update_units(cxt); + break; + case 'v': + verify(cxt); + break; + case 'w': + write_table(cxt); + break; + case 'y': + if (disklabel == SUN_LABEL) + sun_set_pcylcount(cxt); + break; + default: + print_menu(EXPERT_MENU); + } + } +} + +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 +gpt_warning(char *dev) +{ + if (dev && gpt_probe_signature_devname(dev)) + fprintf(stderr, _("\nWARNING: GPT (GUID Partition Table) detected on '%s'! " + "The util fdisk doesn't support GPT. Use GNU Parted.\n\n"), dev); +} + +/* Print disk geometry and partition table of a specified device (-l option) */ +static void print_partition_table_from_option(char *device, unsigned long sector_size) +{ + struct fdisk_context *cxt; + + cxt = fdisk_new_context_from_filename(device, 1); /* read-only */ + if (!cxt) + err(EXIT_FAILURE, _("cannot open %s"), device); + + if (sector_size) /* passed -b option, override autodiscovery */ + fdisk_context_force_sector_size(cxt, sector_size); + + if (user_cylinders || user_heads || user_sectors) + fdisk_context_set_user_geometry(cxt, user_cylinders, + user_heads, user_sectors); + gpt_warning(device); + + if (!fdisk_dev_has_disklabel(cxt)) { + /* + * Try BSD -- TODO: move to list_table() too + */ + list_disk_geometry(cxt); + if (disklabel != AIX_LABEL && disklabel != MAC_LABEL) + btrydev(cxt); + } + else + list_table(cxt, 0); + + fdisk_free_context(cxt); + cxt = NULL; +} + +/* + * for fdisk -l: + * try all things in /proc/partitions that look like a full disk + */ +static void +print_all_partition_table_from_option(unsigned long sector_size) +{ + FILE *procpt; + char line[128 + 1], ptname[128 + 1], devname[256]; + int ma, mi; + unsigned long long sz; + + procpt = fopen(_PATH_PROC_PARTITIONS, "r"); + if (procpt == NULL) { + fprintf(stderr, _("cannot open %s\n"), _PATH_PROC_PARTITIONS); + return; + } + + while (fgets(line, sizeof(line), procpt)) { + if (sscanf (line, " %d %d %llu %128[^\n ]", + &ma, &mi, &sz, ptname) != 4) + continue; + snprintf(devname, sizeof(devname), "/dev/%s", ptname); + if (is_whole_disk(devname)) { + char *cn = canonicalize_path(devname); + if (cn) { + if (!is_ide_cdrom_or_tape(cn)) + print_partition_table_from_option(cn, sector_size); + free(cn); + } + } + } + fclose(procpt); +} + +static void +unknown_command(int c) { + printf(_("%c: unknown command\n"), c); +} + +static void print_welcome(void) +{ + printf(_("Welcome to fdisk (%s).\n\n" + "Changes will remain in memory only, until you decide to write them.\n" + "Be careful before using the write command.\n\n"), PACKAGE_STRING); + + fflush(stdout); +} + +static void command_prompt(struct fdisk_context *cxt) +{ + int c; + + if (disklabel == OSF_LABEL) { + putchar('\n'); + /* OSF label, and no DOS label */ + printf(_("Detected an OSF/1 disklabel on %s, entering " + "disklabel mode.\n"), + cxt->dev_path); + bsd_command_prompt(cxt); + /* If we return we may want to make an empty DOS label? */ + disklabel = DOS_LABEL; + } + + while (1) { + putchar('\n'); + c = tolower(read_char(_("Command (m for help): "))); + switch (c) { + case 'a': + if (disklabel == DOS_LABEL) + toggle_active(get_partition(cxt, 1, partitions)); + else if (disklabel == SUN_LABEL) + toggle_sunflags(cxt, get_partition(cxt, 1, partitions), + SUN_FLAG_UNMNT); + else if (disklabel == SGI_LABEL) + sgi_set_bootpartition(cxt, + get_partition(cxt, 1, partitions)); + else + unknown_command(c); + break; + case 'b': + if (disklabel == SGI_LABEL) + sgi_set_bootfile(cxt); + else if (disklabel == DOS_LABEL) { + disklabel = OSF_LABEL; + bsd_command_prompt(cxt); + disklabel = DOS_LABEL; + } else + unknown_command(c); + break; + case 'c': + if (disklabel == DOS_LABEL) + toggle_dos_compatibility_flag(cxt); + else if (disklabel == SUN_LABEL) + toggle_sunflags(cxt, get_partition(cxt, 1, partitions), + SUN_FLAG_RONLY); + else if (disklabel == SGI_LABEL) + sgi_set_swappartition(cxt, + get_partition(cxt, 1, partitions)); + else + unknown_command(c); + break; + case 'd': + delete_partition(cxt, get_existing_partition(cxt, 1, partitions)); + break; + case 'i': + if (disklabel == SGI_LABEL) + create_sgiinfo(cxt); + else + unknown_command(c); + break; + case 'l': + list_types(get_sys_types()); + break; + case 'm': + print_menu(MAIN_MENU); + break; + case 'n': + new_partition(cxt); + break; + case 'o': + fdisk_create_disklabel(cxt, "dos"); + break; + case 'p': + list_table(cxt, 0); + break; + case 'q': + handle_quit(cxt); + case 's': + fdisk_create_disklabel(cxt, "sun"); + break; + case 't': + change_sysid(cxt); + break; + case 'u': + change_units(cxt); + break; + case 'v': + verify(cxt); + break; + case 'w': + write_table(cxt); + break; + case 'x': + expert_command_prompt(cxt); + break; + default: + unknown_command(c); + print_menu(MAIN_MENU); + } + } +} + +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; +} + +int main(int argc, char **argv) +{ + int c, optl = 0, opts = 0; + unsigned long sector_size = 0; + struct fdisk_context *cxt = NULL; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + while ((c = getopt(argc, argv, "b:c::C:hH:lsS:u::vV")) != -1) { + switch (c) { + case 'b': + /* Ugly: this sector size is really per device, + so cannot be combined with multiple disks, + and te same goes for the C/H/S options. + */ + sector_size = strtou32_or_err(optarg, _("invalid sector size argument")); + if (sector_size != 512 && sector_size != 1024 && + sector_size != 2048 && sector_size != 4096) + usage(stderr); + sector_offset = 2; + break; + case 'C': + user_cylinders = strtou32_or_err(optarg, _("invalid cylinders argument")); + break; + case 'c': + dos_compatible_flag = 0; /* default */ + + if (optarg && !strcmp(optarg, "=dos")) + dos_compatible_flag = ~0; + else if (optarg && strcmp(optarg, "=nondos")) + usage(stderr); + break; + case 'H': + user_heads = strtou32_or_err(optarg, _("invalid heads argument")); + if (user_heads > 256) + user_heads = 0; + break; + case 'S': + user_sectors = strtou32_or_err(optarg, _("invalid sectors argument")); + if (user_sectors >= 64) + user_sectors = 0; + break; + case 'l': + optl = 1; + break; + case 's': + opts = 1; + break; + case 'u': + display_in_cyl_units = 0; /* default */ + if (optarg && strcmp(optarg, "=cylinders") == 0) + display_in_cyl_units = !display_in_cyl_units; + else if (optarg && strcmp(optarg, "=sectors")) + usage(stderr); + break; + case 'V': + case 'v': + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case 'h': + usage(stdout); + default: + usage(stderr); + } + } + + fdisk_init_debug(0); + + if (sector_size && argc-optind != 1) + printf(_("Warning: the -b (set sector size) option should" + " be used with one specified device\n")); + + if (optl) { + nowarn = 1; + if (argc > optind) { + int k; + for (k = optind; k < argc; k++) + print_partition_table_from_option(argv[k], sector_size); + } else + print_all_partition_table_from_option(sector_size); + exit(EXIT_SUCCESS); + } + + if (opts) { + /* print partition size for one or more devices */ + int i, ndevs = argc - optind; + if (ndevs <= 0) + usage(stderr); + + for (i = optind; i < argc; i++) { + if (ndevs == 1) + printf("%llu\n", get_dev_blocks(argv[i])); + else + printf("%s: %llu\n", argv[i], get_dev_blocks(argv[i])); + } + exit(EXIT_SUCCESS); + } + + if (argc-optind != 1) + usage(stderr); + + cxt = fdisk_new_context_from_filename(argv[optind], 0); + if (!cxt) + err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); + + if (sector_size) /* passed -b option, override autodiscovery */ + fdisk_context_force_sector_size(cxt, sector_size); + + if (user_cylinders || user_heads || user_sectors) + fdisk_context_set_user_geometry(cxt, user_cylinders, + user_heads, user_sectors); + + print_welcome(); + + if (!fdisk_dev_sectsz_is_default(cxt)) + printf(_("Note: sector size is %ld (not %d)\n"), + cxt->sector_size, DEFAULT_SECTOR_SIZE); + + gpt_warning(cxt->dev_path); + + if (!fdisk_dev_has_disklabel(cxt)) { + fprintf(stderr, + _("Device does not contain a recognized partition table\n")); + fdisk_create_disklabel(cxt, NULL); + } + + command_prompt(cxt); + + return 0; +} |