/* fdisk.c -- Partition table manipulator for Linux. * * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) * Copyright (C) 2012 Davidlohr Bueso * * 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 #include #include #include #include #include #include #include #include #include #include #include #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 #endif #ifdef HAVE_LINUX_BLKPG_H #include #endif /* 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_("change number of alternate cylinders"), {0, FDISK_DISKLABEL_SUN}}, {'a', N_("select bootable partition"), {FDISK_DISKLABEL_SGI, 0}}, {'a', N_("toggle a bootable flag"), {FDISK_DISKLABEL_DOS, 0}}, {'a', N_("toggle a read only flag"), {FDISK_DISKLABEL_SUN, 0}}, {'b', N_("edit bootfile entry"), {FDISK_DISKLABEL_SGI, 0}}, {'b', N_("edit bsd disklabel"), {FDISK_DISKLABEL_DOS, 0}}, {'b', N_("move beginning of data in a partition"), {0, FDISK_DISKLABEL_DOS}}, {'c', N_("change number of cylinders"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}}, {'c', N_("select sgi swap partition"), {FDISK_DISKLABEL_SGI, 0}}, {'c', N_("toggle the dos compatibility flag"), {FDISK_DISKLABEL_DOS, 0}}, {'c', N_("toggle the mountable flag"), {FDISK_DISKLABEL_SUN, 0}}, {'d', N_("delete a partition"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}}, {'d', N_("print the raw data in the partition table"), {0, FDISK_DISKLABEL_ANY}}, {'e', N_("change number of extra sectors per cylinder"), {0, FDISK_DISKLABEL_SUN}}, {'e', N_("edit drive data"), {FDISK_DISKLABEL_OSF, 0}}, {'e', N_("list extended partitions"), {0, FDISK_DISKLABEL_DOS}}, {'f', N_("fix partition order"), {0, FDISK_DISKLABEL_DOS}}, {'g', N_("create a new empty GPT partition table"), {~FDISK_DISKLABEL_OSF, 0}}, {'g', N_("create an IRIX (SGI) partition table"), {0, FDISK_DISKLABEL_ANY}}, /* for backward compatibility only */ {'G', N_("create an IRIX (SGI) partition table"), {~FDISK_DISKLABEL_OSF, 0}}, {'h', N_("change number of heads"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}}, {'i', N_("change interleave factor"), {0, FDISK_DISKLABEL_SUN}}, {'i', N_("change the disk identifier"), {0, FDISK_DISKLABEL_DOS}}, {'i', N_("install bootstrap"), {FDISK_DISKLABEL_OSF, 0}}, {'l', N_("list known partition types"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}}, {'m', N_("print this menu"), {FDISK_DISKLABEL_ANY, FDISK_DISKLABEL_ANY}}, {'n', N_("add a new partition"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF | FDISK_DISKLABEL_GPT, 0}}, {'o', N_("change rotation speed (rpm)"), {0, FDISK_DISKLABEL_SUN}}, {'o', N_("create a new empty DOS partition table"), {~FDISK_DISKLABEL_OSF, 0}}, {'p', N_("print the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}}, {'q', N_("quit without saving changes"), {FDISK_DISKLABEL_ANY, FDISK_DISKLABEL_ANY}}, {'r', N_("return to main menu"), {FDISK_DISKLABEL_OSF, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF}}, {'s', N_("change number of sectors/track"), {0, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN}}, {'s', N_("create a new empty Sun disklabel"), {~FDISK_DISKLABEL_OSF, 0}}, {'s', N_("show complete disklabel"), {FDISK_DISKLABEL_OSF, 0}}, {'t', N_("change a partition's system id"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}}, {'u', N_("change display/entry units"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI | FDISK_DISKLABEL_OSF, 0}}, {'v', N_("verify the partition table"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI, FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI}}, {'w', N_("write disklabel to disk"), {FDISK_DISKLABEL_OSF, 0}}, {'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}}, {'x', N_("extra functionality (experts only)"), {FDISK_DISKLABEL_DOS | FDISK_DISKLABEL_SUN | FDISK_DISKLABEL_SGI, 0}}, #if !defined (__alpha__) {'x', N_("link BSD partition to non-BSD partition"), {FDISK_DISKLABEL_OSF, 0}}, #endif {'y', N_("change number of physical cylinders"), {0, FDISK_DISKLABEL_SUN}}, }; 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 */ unsigned int user_cylinders, user_heads, user_sectors; void toggle_units(struct fdisk_context *cxt) { fdisk_context_set_unit(cxt, fdisk_context_use_cylinders(cxt) ? "sectors" : "cylinders"); if (fdisk_context_use_cylinders(cxt)) fdisk_info(cxt, _("Changing display/entry units to cylinders (DEPRECATED!).")); else fdisk_info(cxt, _("Changing display/entry units to sectors.")); } static void __attribute__ ((__noreturn__)) usage(FILE *out) { fprintf(out, _("Usage:\n" " %1$s [options] change partition table\n" " %1$s [options] -l list partition table(s)\n" " %1$s -s give partition size(s) in blocks\n" "\nOptions:\n" " -b sector size (512, 1024, 2048 or 4096)\n" " -c[=] compatible mode: 'dos' or 'nondos' (default)\n" " -h print this help text\n" " -u[=] display units: 'cylinders' or 'sectors' (default)\n" " -v print program version\n" " -C specify the number of cylinders\n" " -H specify the number of heads\n" " -S 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 print_menu(struct fdisk_context *cxt, enum menutype menu) { size_t i; int id; puts(_("Command action")); id = cxt && cxt->label ? cxt->label->id : FDISK_DISKLABEL_ANY; for (i = 0; i < ARRAY_SIZE(menulist); i++) if (menulist[i].label[menu] & id) printf(" %c %s\n", menulist[i].command, menulist[i].description); } void list_partition_types(struct fdisk_context *cxt) { struct fdisk_parttype *types; int i; if (!cxt || !cxt->label || !cxt->label->parttypes) return; types = cxt->label->parttypes; if (types[0].typestr == NULL) { /* * Prints in 4 columns in format */ unsigned int last[4], done = 0, next = 0, size; for (i = 0; types[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; struct fdisk_parttype *t = &types[next]; size_t ret; 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 */ struct fdisk_parttype *t; for (i = 0, t = types; t->name; t++, i++) printf("%3d %-30s %s\n", i + 1, t->name, t->typestr); } 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; } int warn_geometry(struct fdisk_context *cxt) { char *m = NULL; int prev = 0; if (fdisk_is_disklabel(cxt, SGI)) /* 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 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); } } static void maybe_exit(struct fdisk_context *cxt, int rc, int *asked) { char line[LINE_LENGTH]; assert(cxt); assert(cxt->label); putchar('\n'); if (asked) *asked = 0; if (fdisk_label_is_changed(cxt->label)) { fprintf(stderr, _("Do you really want to quit? ")); if (!fgets(line, LINE_LENGTH, stdin) || rpmatch(line) == 1) goto leave; if (asked) *asked = 1; return; } leave: fdisk_free_context(cxt); exit(rc); } /* read line; return 0 or first char */ int read_line(struct fdisk_context *cxt, int *asked) { line_ptr = line_buffer; if (!fgets(line_buffer, LINE_LENGTH, stdin)) { maybe_exit(cxt, 1, asked); return 0; } if (asked) *asked = 0; while (*line_ptr && !isgraph(*line_ptr)) line_ptr++; return *line_ptr; } char read_char(struct fdisk_context *cxt, char *mesg) { do { fputs(mesg, stdout); fflush (stdout); /* requested by niles@scyld.com */ } while (!read_line(cxt, NULL)); return *line_ptr; } char read_chars(struct fdisk_context *cxt, char *mesg) { int rc, asked = 0; do { fputs(mesg, stdout); fflush (stdout); /* niles@scyld.com */ rc = read_line(cxt, &asked); } while (asked); if (!rc) { *line_ptr = '\n'; line_ptr[1] = 0; } return *line_ptr; } struct fdisk_parttype *read_partition_type(struct fdisk_context *cxt) { if (!cxt || !cxt->label || !cxt->label->nparttypes) return NULL; do { size_t sz; if (cxt->label->parttypes[0].typestr) read_chars(cxt, _("Partition type (type L to list all types): ")); else read_chars(cxt, _("Hex code (type L to list all codes): ")); sz = strlen(line_ptr); if (!sz || line_ptr[sz - 1] != '\n' || sz == 1) continue; line_ptr[sz - 1] = '\0'; if (tolower(*line_ptr) == 'l') list_partition_types(cxt); else return fdisk_parse_parttype(cxt, line_ptr); } while (1); return NULL; } 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(cxt, 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 (fdisk_context_use_cylinders(cxt)) 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 * fdisk_context_get_units_per_sector(cxt); 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 && !fdisk_is_disklabel(cxt, GPT)) { if ((!fdisk_is_disklabel(cxt, SUN) && !fdisk_is_disklabel(cxt, SGI) && !pe->part_table->sys_ind) || (fdisk_is_disklabel(cxt, SUN) && sun_is_empty_type(cxt, i)) || (fdisk_is_disklabel(cxt, SGI) && (!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 (!fdisk_is_disklabel(cxt, DOS)) 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); } static 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); if (flag) printf(_("DOS Compatibility flag is set (DEPRECATED!)\n")); else printf(_("DOS Compatibility flag is not set\n")); fdisk_dos_enable_compatible(lb, flag); if (fdisk_is_disklabel(cxt, DOS)) fdisk_reset_alignment(cxt); } static void delete_partition(struct fdisk_context *cxt, int partnum) { if (partnum < 0 || warn_geometry(cxt)) return; ptes[partnum].changed = 1; if (fdisk_delete_partition(cxt, partnum) != 0) printf(_("Could not delete partition %d\n"), partnum + 1); else printf(_("Partition %d is deleted\n"), partnum + 1); } static void change_partition_type(struct fdisk_context *cxt) { int i; struct fdisk_parttype *t, *org_t; assert(cxt); assert(cxt->label); i = get_existing_partition(cxt, 0, cxt->label->nparts_max); if (i == -1) return; org_t = t = fdisk_get_partition_type(cxt, i); if (!t) printf(_("Partition %d does not exist yet!\n"), i + 1); else do { t = read_partition_type(cxt); if (!t) continue; if (fdisk_set_partition_type(cxt, i, t) == 0) { ptes[i].changed = 1; printf (_("Changed type of partition '%s' to '%s'\n"), org_t ? org_t->name : _("Unknown"), t ? t->name : _("Unknown")); } else { printf (_("Type of partition %d is unchanged: %s\n"), i + 1, org_t ? org_t->name : _("Unknown")); } break; } while (1); fdisk_free_parttype(t); fdisk_free_parttype(org_t); } 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 (is_dos_compatible(cxt)) 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"), 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); 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 (fdisk_dev_has_disklabel(cxt)) printf(_("Disk label type: %s\n"), cxt->label->name); if (fdisk_is_disklabel(cxt, DOS)) dos_print_mbr_id(cxt); printf("\n"); } static void list_table(struct fdisk_context *cxt, int xtra) { if (fdisk_is_disklabel(cxt, SUN)) { sun_list_table(cxt, xtra); return; } if (fdisk_is_disklabel(cxt, SGI)) { sgi_list_table(cxt, xtra); return; } list_disk_geometry(cxt); if (fdisk_is_disklabel(cxt, GPT)) { gpt_list_table(cxt, xtra); return; } if (fdisk_is_disklabel(cxt, OSF)) { xbsd_print_disklabel(cxt, xtra); return; } if (fdisk_is_disklabel(cxt, DOS)) dos_list_table(cxt, xtra); } 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); struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid); printf(_("Partition %d of type %s and of size %s is set\n"), num, t ? t->name : _("Unknown"), str); free(str); } static void new_partition(struct fdisk_context *cxt) { assert(cxt); assert(cxt->label); if (warn_geometry(cxt)) return; fdisk_add_partition(cxt, NULL); } 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 (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) { size_t i; assert(cxt); assert(cxt->label); printf(_("Device: %s\n"), cxt->dev_path); if (fdisk_is_disklabel(cxt, SUN) || fdisk_is_disklabel(cxt, SGI) || fdisk_is_disklabel(cxt, GPT)) print_buffer(cxt, cxt->firstsector); else for (i = 3; i < cxt->label->nparts_max; i++) print_buffer(cxt, ptes[i].sectorbuffer); } 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; assert(cxt); while(1) { assert(cxt->label); putchar('\n'); c = tolower(read_char(cxt, _("Expert command (m for help): "))); switch (c) { case 'a': if (fdisk_is_disklabel(cxt, SUN)) sun_set_alt_cyl(cxt); break; case 'b': if (fdisk_is_disklabel(cxt, DOS)) dos_move_begin(cxt, get_partition(cxt, 0, cxt->label->nparts_max)); break; case 'c': user_cylinders = read_int(cxt, 1, cxt->geom.cylinders, 1048576, 0, _("Number of cylinders")); fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors); if (fdisk_is_disklabel(cxt, SUN)) sun_set_ncyl(cxt, cxt->geom.cylinders); break; case 'd': print_raw(cxt); break; case 'e': if (fdisk_is_disklabel(cxt, SGI)) sgi_set_xcyl(); else if (fdisk_is_disklabel(cxt, SUN)) sun_set_xcyl(cxt); else if (fdisk_is_disklabel(cxt, DOS)) dos_list_table_expert(cxt, 1); break; case 'f': if (fdisk_is_disklabel(cxt, DOS)) dos_fix_partition_table_order(cxt); break; case 'g': fdisk_create_disklabel(cxt, "sgi"); break; case 'h': user_heads = read_int(cxt, 1, cxt->geom.heads, 256, 0, _("Number of heads")); fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors); break; case 'i': if (fdisk_is_disklabel(cxt, SUN)) sun_set_ilfact(cxt); else if (fdisk_is_disklabel(cxt, DOS)) dos_set_mbr_id(cxt); break; case 'o': if (fdisk_is_disklabel(cxt, SUN)) sun_set_rspeed(cxt); break; case 'p': if (fdisk_is_disklabel(cxt, SUN)) list_table(cxt, 1); else dos_list_table_expert(cxt, 0); break; case 'q': handle_quit(cxt); case 'r': return; case 's': user_sectors = read_int(cxt, 1, cxt->geom.sectors, 63, 0, _("Number of sectors")); if (is_dos_compatible(cxt)) fprintf(stderr, _("Warning: setting " "sector offset for DOS " "compatibility\n")); fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors); break; case 'v': verify(cxt); break; case 'w': write_table(cxt); break; case 'y': if (fdisk_is_disklabel(cxt, SUN)) sun_set_pcylcount(cxt); break; default: print_menu(cxt, 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; } /* Print disk geometry and partition table of a specified device (-l option) */ static void print_partition_table_from_option(struct fdisk_context *cxt, char *device, unsigned long sector_size) { if (fdisk_context_assign_device(cxt, device, 1) != 0) /* read-only */ err(EXIT_FAILURE, _("cannot open %s"), device); if (sector_size) /* passed -b option, override autodiscovery */ fdisk_override_sector_size(cxt, sector_size); if (user_cylinders || user_heads || user_sectors) fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors); if (!fdisk_dev_has_disklabel(cxt)) { /* * Try BSD -- TODO: move to list_table() too */ list_disk_geometry(cxt); if (!fdisk_is_disklabel(cxt, AIX) && !fdisk_is_disklabel(cxt, MAC)) btrydev(cxt); } else list_table(cxt, 0); } /* * for fdisk -l: * try all things in /proc/partitions that look like a full disk */ static void print_all_partition_table_from_option(struct fdisk_context *cxt, 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(cxt, 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; assert(cxt); if (fdisk_is_disklabel(cxt, OSF)) { 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? */ fdisk_context_switch_label(cxt, "dos"); } while (1) { assert(cxt->label); putchar('\n'); c = tolower(read_char(cxt, _("Command (m for help): "))); switch (c) { case 'a': if (fdisk_is_disklabel(cxt, DOS)) dos_toggle_active(cxt, get_partition(cxt, 1, cxt->label->nparts_max)); else if (fdisk_is_disklabel(cxt, SUN)) toggle_sunflags(cxt, get_partition(cxt, 1, cxt->label->nparts_max), SUN_FLAG_UNMNT); else if (fdisk_is_disklabel(cxt, SGI)) sgi_set_bootpartition(cxt, get_partition(cxt, 1, cxt->label->nparts_max)); else unknown_command(c); break; case 'b': /* * TODO: create child context for nexted partition tables */ if (fdisk_is_disklabel(cxt, SGI)) sgi_set_bootfile(cxt); else if (fdisk_is_disklabel(cxt, DOS)) { fdisk_context_switch_label(cxt, "bsd"); bsd_command_prompt(cxt); fdisk_context_switch_label(cxt, "dos"); } else unknown_command(c); break; case 'c': if (fdisk_is_disklabel(cxt, DOS)) toggle_dos_compatibility_flag(cxt); else if (fdisk_is_disklabel(cxt, SUN)) toggle_sunflags(cxt, get_partition(cxt, 1, cxt->label->nparts_max), SUN_FLAG_RONLY); else if (fdisk_is_disklabel(cxt, SGI)) sgi_set_swappartition(cxt, get_partition(cxt, 1, cxt->label->nparts_max)); else unknown_command(c); break; case 'd': delete_partition(cxt, get_existing_partition(cxt, 1, cxt->label->nparts_max)); break; case 'g': fdisk_create_disklabel(cxt, "gpt"); break; case 'G': fdisk_create_disklabel(cxt, "sgi"); break; case 'i': if (fdisk_is_disklabel(cxt, SGI)) create_sgiinfo(cxt); else unknown_command(c); break; case 'l': list_partition_types(cxt); break; case 'm': print_menu(cxt, 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_partition_type(cxt); break; case 'u': toggle_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(cxt, 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; struct fdisk_label *lb; 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: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); break; case 'C': user_cylinders = strtou32_or_err(optarg, _("invalid cylinders argument")); break; case 'c': if (optarg) { 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': 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': 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 (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(cxt, argv[k], sector_size); } else print_all_partition_table_from_option(cxt, 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); if (fdisk_context_assign_device(cxt, argv[optind], 0) != 0) err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); if (sector_size) /* passed -b option, override autodiscovery */ fdisk_override_sector_size(cxt, sector_size); if (user_cylinders || user_heads || user_sectors) fdisk_override_geometry(cxt, user_cylinders, user_heads, user_sectors); print_welcome(); 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); fdisk_free_context(cxt); return 0; }