/* * Changelog: * 25/03/1998 RMK Added option for selecting partitioning scheme */ #include <assert.h> #include <errno.h> #include <ctype.h> #include <setjmp.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/fcntl.h> #include <sys/ioctl.h> #include <sys/mount.h> #include "part/part.h" #include "util/error.h" #define NR_DEVICES 12 #define DEFAULT_DEVICE "/dev/hda" #define ALTERNATE_DEVICE "/dev/sda" static const char *part_devs[NR_DEVICES] = { "/dev/hda", "/dev/hdb", "/dev/hdc", "/dev/hdd", "/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd", "/dev/sde", "/dev/sdf", "/dev/sdg", "/dev/sdh" }; #if 1 #include <getopt.h> static const char *device; static const char *progname; static const char *dev_part_type = ""; static part_t *part; typedef enum { mode_partition, mode_version, mode_help, mode_list, mode_size, mode_illegal } prog_mode_t; static struct option options[] = { { "help", no_argument, NULL, 'h' }, { "list", no_argument, NULL, 'l' }, { "size", required_argument, NULL, 's' }, { "type", required_argument, NULL, 't' }, { "version", no_argument, NULL, 'v' }, { NULL, no_argument, NULL, 0 } }; #define is_extended(x) ((x) == ptyp_dos_extended || (x) == ptyp_linux_extended) static prog_mode_t parse_args(int argc, const char *argv[]) { prog_mode_t mode = mode_partition; int opt; do { opt = getopt_long(argc, argv, "s:l", options, NULL); switch(opt) { case 'h': mode = mode_help; opt = EOF; break; case 'v': mode = mode_version; opt = EOF; break; case 'l': mode = mode_list; break; case 's': mode = mode_size; device = optarg; break; case 't': dev_part_type = optarg; break; case EOF: break; default: mode = mode_illegal; opt = EOF; break; } } while (opt != EOF); return mode; } static char buf[16]; static const char * read_line(const char *msg, ...) { va_list ap; char *p; buf[0] = '\0'; do { va_start(ap, msg); vprintf(msg, ap); va_end(ap); fgets(buf, sizeof(buf) - 1, stdin); } while (!feof(stdin) && buf[0] == '\0'); p = strchr(buf, '\n'); if (p) *p = '\0'; return buf; } static char read_char(const char *msg) { return read_line(msg)[0]; } static void list_types(u_int parn, ptyp_t next) { ptyp_t types[256], cur; u_int idx = 0, last[4], done = 0, nxt; int i; do { cur = next; next = part_nexttype(part, parn, cur, -1); } while (cur != next); do { cur = next; types[idx++] = cur; next = part_nexttype(part, parn, cur, +1); } while (cur != next); for (i = 3; i >= 0; i--) last[3 - i] = done += (idx + i - done) / (i + 1); i = nxt = done = 0; do { printf("%c%3x %-15.15s", i ? ' ' : '\n', types[nxt], part_typename(part, types[nxt])); nxt = last[i++] + done; if (i > 3 || nxt >= last[i]) { i = 0; nxt = ++done; } } while (done < last[0]); putchar('\n'); } static ptyp_t read_type(u_int parn, ptyp_t old_type, const char *prompt) { int hex; while (1) { const char *line = read_line(prompt); if (tolower(line[0]) == 'l') list_types(parn, old_type); else if (isxdigit(line[0])) { hex = (int)strtoul(line, NULL, 16); break; } } return (ptyp_t)hex; } static u_int get_part_nr(void) { buf[0] = '\0'; do { fputs("Which partition? ", stdout); fgets(buf, sizeof(buf) - 1, stdin); } while (!feof(stdin) && buf[0] == '\0'); return atoi(buf); } static u_int display_factor = 1; /* in units/sector */ static u_int full_bits = 0; /* 1024 cylinders in sectors */ static bool_t unit_flag = 1; static void update_units(part_t *part) { geometry_t geo; part_getgeometry(part, &geo); full_bits = 1024 * geo.heads * geo.sectors; if (unit_flag && full_bits) display_factor = full_bits >> 10; else display_factor = 1; } static void change_units(part_t *part) { unit_flag = !unit_flag; if (part) update_units(part); printf("Changing display/entry units to %ss\n", unit_flag ? "cylinder" : "sector"); } static u_int sectors_to_units(u_int sectors) { return (sectors + (unit_flag ? display_factor - 1 : 0)) / display_factor; } static u_int units_to_sectors(u_int units) { return units * display_factor; } static inline u_int calculate(part_t *part, u_int head, u_int sect, u_int cyl) { geometry_t geo; part_getgeometry(part, &geo); return sect - 1 + geo.sectors * (head + geo.heads * cyl); } static void delete_partition(void) { partinfo_t info; u_int part_no, found = 0, partition = get_part_nr(); for (part_no = 0; part_getpartinfo(part, part_no, &info); part_no += 1) if (info.kern_part_no == partition) { found = 1; break; } if (!found) { printf("Bad partition number `%d'\n", partition); return; } if (!(part_validops(part, part_no, &info) & VALIDOPS_DELETE)) { printf("Partition %d cannot be deleted.\n", info.kern_part_no); return; } if (!part_delete(part, part_no)) printf("Unable to delete partition `%d': %s\n", partition, get_error()); else printf("Partition `%d' deleted\n", partition); } struct regions { struct { u_int64_t start; u_int64_t end; } region[16]; u_int nr; }; static void add_region(struct regions *region, partinfo_t *info) { u_int i; for (i = 0; i < region->nr; i++) { if (region->region[i].start > info->blk_start) { memmove(®ion->region[i + 1], ®ion->region[i], (region->nr - i) * sizeof(region->region[0])); region->region[i].start = info->blk_start; region->region[i].end = info->blk_end + 1; region->nr += 1; goto added; } } region->region[region->nr].start = info->blk_start; region->region[region->nr].end = info->blk_end + 1; region->nr += 1; added: for (i = 1; i < region->nr;) { if (region->region[i].start == region->region[i - 1].end) { region->region[i - 1].end = region->region[i].end; memmove(®ion->region[i], ®ion->region[i+1], (region->nr - i) * sizeof(region->region[0])); region->nr -= 1; } else i += 1; } } static void get_regions(struct regions *region) { partinfo_t info; u_int parn = 0; region->nr = 0; while (part_getpartinfo(part, parn++, &info)) { if (!info.type) continue; add_region(region, &info); } } static void new_partition(void) { partinfo_t info; geometry_t geo; struct regions regions; u_int parn, total, first = 1, i; char *units; update_units(part); part_getgeometry(part, &geo); total = geo.sectors * geo.heads * geo.cylinders; if (unit_flag) units = "cylinder"; else units = "sector"; printf("\nUnits are %ss of %d * %d bytes\nDisk space used:", units, display_factor, 512); get_regions(®ions); for (i = 0; i < regions.nr; i++) printf("%s %d - %d", i ? "," : "", sectors_to_units(regions.region[i].start), sectors_to_units(regions.region[i].end)); printf("\nTotal disk: %d\n\n", sectors_to_units(total)); parn = part_allocate(part, &info); if (parn == PARN_ERROR) { printf("Unable to allocate a new partition slot: %s\n", get_error()); return; } printf("Allocating partition %s%d\n\n", device, info.kern_part_no); do { const char *l; l = read_line(first ? "Start %s : " : "Start %s (%u) : ", units, sectors_to_units(info.blk_start)); if (isdigit(l[0])) info.blk_start = units_to_sectors(strtoul(l, NULL, 10)); else if (first || l[0] == 'q') { printf("\nPartition not created\n"); return; } l = read_line(first ? "End %s : " : "End %s (%u) : ", units, sectors_to_units(info.blk_end + 1)); if (l[0] == '+') info.blk_end = info.blk_start + units_to_sectors(strtoul(l + 1, NULL, 10)) - 1; else if (isdigit(l[0])) info.blk_end = units_to_sectors(strtoul(l, NULL, 10)) - 1; else if (first || l[0] == 'q') { printf("\nPartition not created\n"); return; } info.chs_valid = 0; first = 0; if (info.blk_end > info.blk_start && info.blk_end < total) { if (part_create(part, parn, &info)) { printf("\nPartition %d created\n", parn); return; } else printf("\nUnable to create partition: %s\n\n", get_error()); } else printf("\nInvalid partition start/end\n\n"); } while (1); } static void change_type(void) { partinfo_t info; u_int part_no, found = 0, partition = get_part_nr(); ptyp_t old_type, new_type; for (part_no = 0; part_getpartinfo(part, part_no, &info); part_no += 1) if (info.kern_part_no == partition) { found = 1; break; } if (!found) { printf("Bad partition number `%d'\n", partition); return; } if (!(part_validops(part, part_no, &info) & VALIDOPS_UPDATE)) { printf("Partition %d cannot be altered.\n", info.kern_part_no); return; } old_type = info.type; if (is_extended(info.type)) { printf("Partition %d is extended. Delete it\n", info.kern_part_no); return; } new_type = read_type(part_no, old_type, "Hex code (type L to list codes):"); if (is_extended(new_type)) { printf("You may not change a partition " "to be an extended partition\n"); return; } info.type = new_type; if (!part_setpartinfo(part, part_no, &info)) printf("Unable to change partition %d type: %s\n", partition, get_error()); else printf("Partition `%d' type changed from %X (%s) to %X (%s)\n", partition, old_type, part_typename(part, old_type), new_type, part_typename(part, new_type)); } typedef enum { LIST_NORMAL, LIST_EXTENDED_NORMAL, LIST_EXTENDED_EXTENDED } list_t; static inline int is_displayable_partition(ptyp_t type) { return type != ptyp_none && type != ptyp_pc_table; } static void list_table(list_t list_type) { partinfo_t info; geometry_t geo; int i, w = 0; update_units(part); part_getgeometry(part, &geo); printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n", device, geo.heads, geo.sectors, geo.cylinders); if (list_type == LIST_NORMAL) { printf("Units = %ss of ", unit_flag ? "cylinder" : "sector"); if (display_factor > 1) printf("%d * ", display_factor); printf("512 bytes\n\n"); w = strlen(device) + 1; if (w < 6) w = 6; printf("%*s Boot Begin Start End Blocks Id System\n", w, "Device"); } else printf("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n"); i = 0; while (part_getpartinfo(part, i++, &info)) { if (list_type == LIST_NORMAL && !is_displayable_partition(info.type)) continue; if (list_type == LIST_NORMAL) { printf("%*s%-2d %c%9d%9d%9d%11lld%c %4x %s\n", w - 1, device, info.kern_part_no, ' ', sectors_to_units(calculate(part, info.chs_start.head, info.chs_start.sector, info.chs_start.cylinder)), sectors_to_units(info.blk_start), sectors_to_units(info.blk_end + (info.blk_end & 1 ? 0 : 1)), (info.blk_end - info.blk_start + 1) / 2, (info.blk_end - info.blk_start + 1) & 1 ? '+' : ' ', info.type, part_typename(part, info.type)); // check_consistency (part, p); } else { printf("%2d %02x%4d%4d%5d%4d%4d%5d%12lld%12lld %03x\n", info.kern_part_no, 0, info.chs_start.head, info.chs_start.sector, info.chs_start.cylinder, info.chs_end.head, info.chs_end.sector, info.chs_end.cylinder, info.blk_start, info.blk_end - info.blk_start + 1, info.type); // if (info.type != ptyp_none) // check_consistency (part, p); } } } typedef enum { pps_ret_ok, pps_ret_open_error, pps_ret_not_found } pps_ret_t; static pps_ret_t print_part_size(const char *device) { pps_ret_t ret = pps_ret_not_found; char dev_nam[16], *p; part_t *part; int partition; strncpy(dev_nam, device, 15); dev_nam[15] = '\0'; for (p = dev_nam; *p && !isdigit(*p); p++); partition = atoi(p); *p = '\0'; part = part_open(dev_nam, dev_part_type); if (part) { int i = 0; partinfo_t info; while(part_getpartinfo(part, i, &info)) { if (info.kern_part_no == partition) { printf("%lld\n", (info.blk_end - info.blk_start + 1) / 2); ret = pps_ret_ok; break; } i += 1; } part = part_close(part); } else ret = pps_ret_not_found; return ret; } /* Prototype: int print_part_tables(int nr, const char **parts) * Purpose : Display a list of partition tables * Params : nr - number of partition tables to display * : parts - pointers to partition table names * Returns : 0 if success */ static int print_part_tables(int nr, const char **parts) { int i, ret = 1; for(i = 0; i < nr; i++) { device = parts[i]; part = part_open(device, dev_part_type); if (part) { list_table(LIST_NORMAL); part = part_close(part); ret = 0; } } return ret; } static void write_tables(void) { const char *l; int i, error = 0; printf("\nWriting the partition tables is dangerous and" "\ncan result in loss of data. Please confirm" "\nthat you really want to do this by typing 'YES'" "\nexactly as shown\n\n"); l = read_line("Confirm: "); if (strcmp(l, "YES")) { printf("\nCONFIRMATION NOT GIVEN - not updating partition table\n"); return; } if (!part_sync(part)) { printf("\nERROR occurred while updating partition tables: %s\n", get_error()); return; } printf("The partition table has been altered!\n\n"); printf("Syncing disks..."); fflush(stdout); sync(); sleep(2); sync(); printf("OK\nRe-reading the partition table..."); fflush(stdout); i = ioctl(part->blkio->fd, BLKRRPART); if (i != 0) error = errno; else { printf(" resyncing..."); fflush(stdout); sync(); sleep(2); i = ioctl(part->blkio->fd, BLKRRPART); if (i != 0) error = errno; } if (i < 0) printf("FAILED!\n"); printf("Syncing disks..."); fflush(stdout); sync(); sleep(4); printf("OK\n"); if (i < 0) printf("\n*** Re-read table failed with error %d: %s ***\n" " Reboot your system to ensure the partition table is\n" " updated.\n", error, strerror(error)); exit(0); } static void show_main_menu (void) { printf("\nCommand action\n" " d delete a partition\n" // " l list known partition types\n" " m print this menu\n" " n add a new partition\n" " p print the partition table\n" " q quit without saving changes\n" " r reopen partition, specifing type\n" " t change a partition's system id\n" " u change display/entry units\n" // " v verify the partition table\n" " w write table to disk and exit\n" // " x extra functionality (experts only)\n" ); } /* Prototype: void main_menu(void) * Purpose : display main menu & process user selections */ static void main_menu(void) { int quit = 0; part_t *new_part; const char *s; static char *scheme; show_main_menu(); do { putchar('\n'); switch (tolower(read_char("Command (m for help): "))) { case 'd': if (part) delete_partition(); else goto unavailable; break; case 'e': if (part) list_table(LIST_EXTENDED_NORMAL); else goto unavailable; break; // case 'l': // if (part) // list_types(); // else // goto unavailable; // break; case 'm': default: show_main_menu(); break; case 'n': if (part) new_partition(); else goto unavailable; break; case 'p': if (part) list_table(LIST_NORMAL); else goto unavailable; break; case 'q': quit = 1; break; case 'r': s = read_line("Partitioning scheme: "); new_part = part_open(device, s); if (new_part) { if (scheme) free(scheme); part_close(part); part = new_part; dev_part_type = scheme = strdup(s); fprintf(stderr, "%s successfully reopened using %s scheme\n", device, dev_part_type); } else if (part) fprintf(stderr, "%s: %s: unable to reopen partition as %s,\n" "using %s instead.\n", progname, device, s, dev_part_type); else fprintf(stderr, "%s; %s: unable to reopen partition as %s\n", progname, device, s); break; case 't': if (part) change_type(); else goto unavailable; break; case 'u': change_units(part); break; case 'w': if (part) { write_tables(); break; } unavailable: fprintf(stderr, "%s: %s: option ignored - please re-open the device\n", progname, device); break; } } while (!quit); } /* Prototype: u_int edit_device(const char *dev_nam) * Purpose : Edit partition tables on a device * Params : dev_nam - special device name to edit * Returns : 0 if success */ static u_int edit_device(const char *dev_nam) { u_int use_default = dev_nam == NULL; if (use_default) { device = DEFAULT_DEVICE; part = part_open(device, dev_part_type); if (!part) { device = ALTERNATE_DEVICE; part = part_open(ALTERNATE_DEVICE, dev_part_type); } } else { device = dev_nam; part = part_open(device, dev_part_type); } if (use_default) printf("\nUsing %s as default device\n", device); if (!part) printf("Unable to open device %s: %s\n\n" " Please use 'r' to open the partition as a specific type\n\n", device, get_error()); else printf("\nDevice %s is partitioned using %s scheme\n", device, part_getscheme(part)); main_menu(); return 0; } int main(int argc, const char *argv[]) { prog_mode_t mode; u_int ret = 1; progname = argv[0]; mode = parse_args(argc, argv); switch(mode) { case mode_version: printf("arm-fdisk v"VERSION"\n"); break; case mode_help: printf("Usage: %s [OPTION] [DEVICE]...\n", progname); printf("Display or edit partition tables on block devices.\n\n"); printf(" --type SCHEME specify the partitioning scheme to use on the device\n"); printf(" -l, --list DEVICE... list partition information on devices\n"); printf(" or all devices\n"); printf(" -s, --size DEVICE display size of partition\n"); printf(" --help display this help and exit\n"); printf(" --version output version information and exit\n\n"); printf("With no DEVICE, partition /dev/hda or /dev/sda\n"); break; case mode_size: switch(print_part_size(device)) { case pps_ret_ok: ret = 0; break; case pps_ret_open_error: fprintf(stderr, "Cannot open %s: %s\n", device, get_error()); break; case pps_ret_not_found: ret = 1; break; } break; case mode_list: if (optind < argc) ret = print_part_tables(argc - optind, argv + optind); else ret = print_part_tables(NR_DEVICES, part_devs); break; case mode_partition: if (optind < argc - 1) { fprintf(stderr, "Partitioning is only allowed on a single device\n"); goto bad; } if (optind == argc) ret = edit_device(NULL); else ret = edit_device(argv[optind]); break; bad: case mode_illegal: fprintf(stderr, "Try `%s --help` for more information.\n", progname); ret = 1; break; } return ret; } #else #include "partitions.h" #define LINE_LENGTH 80 #define NR_DEF_DEVS 2 static const char *default_devs[NR_DEF_DEVS] = { DEFAULT_DEVICE, ALTERNATE_DEVICE }; static const char *usage = "Usage: fdisk [-l [device...]] [-v] [-s /dev/hdxn] [/dev/hdx]\n"; static char *prog_name; static bool errorjump_set; static jmp_buf errorjump; unsigned int sector_offset = 1; bool dos_compatible_flag = TRUE; /* * Sundry functions... */ #ifdef __GNUC__ static volatile void fatal (const char *message, ...) __attribute__((noreturn)); #endif static volatile void fatal (const char *message, ...) { va_list ap; if (errorjump_set) longjmp (errorjump, 1); va_start (ap, message); fprintf (stderr, "%s: ", prog_name); vfprintf (stderr, message, ap); va_end (ap); exit (1); } static char line_buffer[LINE_LENGTH]; static char *line_ptr; /* interactive input */ char read_line (void) { if (!fgets (line_buffer, LINE_LENGTH, stdin)) return 0; line_ptr = line_buffer; while (*line_ptr && !isgraph (*line_ptr)) line_ptr ++; return *line_ptr; } char read_char (char *mesg) { do fputs (mesg, stdout); while (!read_line ()); return *line_ptr; } #define hex_val(c) ({ char _c = (c); isdigit(_c) ? _c - '0' : tolower(_c) + 10 - 'a'; }) int read_hex (struct systypes *sys, int size) { int hex; while (1) { read_char ("Hex code (type L to list codes): "); if (tolower (*line_ptr) == 'l') part_list_types (sys, size); else if (isxdigit (*line_ptr)) { hex = 0; do hex = hex << 4 | hex_val (*line_ptr++); while (isxdigit (*line_ptr)); return hex; } } } enum offset { BASE_IGNORE, BASE_LOWER, BASE_UPPER, BASE_DEFAULT }; int read_int (Partition_t *part, unsigned int low, unsigned int dflt, unsigned int high, enum offset base, char *mesg) { unsigned int i, use_default = 1; char ms[70]; switch (base) { case BASE_LOWER: sprintf (ms, "%s ([%d]-%d): ", mesg, low, high); break; case BASE_UPPER: sprintf (ms, "%s (%d-[%d]): ", mesg, low, high); break; case BASE_DEFAULT: sprintf (ms, "%s: (%d-[%d]-%d): ", mesg, low, dflt, high); break; default: sprintf (ms, "%s: (%d-%d): ", mesg, low, high); break; } while (1) { while (!isdigit (read_char (ms)) && (*line_ptr != '-' && *line_ptr != '+')) continue; if (*line_ptr == '+' || *line_ptr == '-') { if (*line_ptr == '+') line_ptr ++; i = atoi (line_ptr); while (isdigit (*line_ptr)) { line_ptr ++; use_default = 0; } switch (*line_ptr) { case 'c': case 'C': if (!unit_flag) i *= part->geo.heads * part->geo.sectors; break; case 'k': case 'K': i = (i << 1) / display_factor; break; case 'm': case 'M': i = (i << 11) / display_factor; break; default: break; } switch (base) { case BASE_LOWER: i += low; break; case BASE_UPPER: i += high; break; case BASE_DEFAULT: i += dflt; default: break; } } else { i = atoi (line_ptr); while (isdigit (*line_ptr)) { line_ptr ++; use_default = 0; } } if (use_default) printf ("Using default value %d\n", i = dflt); if (i >= low && i <= high) break; else printf ("Value out of range.\n"); } return i; } int select_partition (Partition_t *partitions, bool warn, int min, int max) { int i; if (!max) { min = 1; for (i = 0; i < partitions->nr_parts; i++) if (max < partitions->parts[i].dev_index) max = partitions->parts[i].dev_index; } max = read_int (partitions, min, max, max, BASE_IGNORE, "Partition number"); if (warn) { for (i = 0; i < partitions->nr_parts; i++) if (max == partitions->parts[i].dev_index) break; if (i >= partitions->nr_parts) printf ("Warning: partition %d has empty type\n", max); } return max; } void log2chs (Partition_t *part, unsigned long ls, unsigned int *c, unsigned int *h, unsigned int *s) { int spc = part->geo.heads * part->geo.sectors; *c = ls / spc; ls = ls % spc; *h = ls / part->geo.sectors; *s = ls % part->geo.sectors + 1; } void check_bounds (Partition_t *part, unsigned long *first, unsigned long *last) { int i; assert (part != NULL); assert (first != NULL); assert (last != NULL); for (i = 0; i < part->nr_parts; i++) { Part_t *p = &part->parts[i]; first[i] = calculate (part, p->begin.heads, p->begin.sectors, p->begin.cylinders); last[i] = p->start + p->size - 1; } } static void check_consistency (Partition_t *part, Part_t *p) { unsigned int pbc, pbh, pbs; unsigned int pec, peh, pes; unsigned int lbc, lbh, lbs; unsigned int lec, leh, les; /* physical beginning CHS */ pbc = p->begin.cylinders; pbh = p->begin.heads; pbs = p->begin.sectors; /* physical end CHS */ pec = p->end.cylinders; peh = p->end.heads; pes = p->end.sectors; /* logical beginning CHS */ log2chs (part, p->start, &lbc, &lbh, &lbs); /* logical end CHS */ log2chs (part, p->start + p->size - 1, &lec, &leh, &les); if (part->geo.cylinders <= 1024) { if (pbc != lbc || pbh != lbh || pbs != lbs) { printf ("Partition %d has different physical/logical " "beginnings (non-Linux?):\n", p->dev_index); printf (" phys=(%d, %d, %d) ", pbc, pbh, pbs); printf (" logi=(%d, %d, %d)\n", lbc, lbh, lbs); } if (pec != lec || peh != leh || pes != les) { printf ("Partition %d has different physical/logical " "endings:\n", p->dev_index); printf (" phys=(%d, %d, %d) ", pec, peh, pes); printf (" logi=(%d, %d, %d)\n", lec, leh, les); } } if (p->must_start_cyl && (pbh != 0/*!pbc*/ || pbs != 1)) { printf ("Partition %d does not start on a cylinder boundary:\n", p->dev_index); printf (" phys=(%d, %d, %d) ", pbc, pbh, pbs); printf ("should be (%d, %d, 1)\n", pbc, 0/*!pbc*/); } if (p->must_end_cyl && (peh != part->geo.heads - 1 || pes != part->geo.sectors)) { printf ("Partition %d does not end on a cylinder boundary:\n", p->dev_index); printf (" phys=(%d, %d, %d) ", pec, peh, pes); printf ("should be (%d, %d, %d)\n", pec, part->geo.heads - 1, part->geo.sectors); } if (p->must_1mb && p->size % 1048576) printf ("Partition %d does not contain a whole number of MB\n", p->dev_index); } void check (Partition_t *part, Part_t *p, unsigned long start) { /* if (!total) printf ("Warning: partition %d contains sector 0\n", p->dev_index); */ if (p->end.heads >= part->geo.heads) printf ("Partition %d: head %d is greater than maximum %d\n", p->dev_index, p->end.heads, part->geo.heads - 1); if (p->end.sectors > part->geo.sectors) printf ("Partition %d; sector %d is greater than maximum %d\n", p->dev_index, p->end.sectors, part->geo.sectors); if (p->end.cylinders >= part->geo.cylinders) printf ("Partition %d: cylinder %d is greater than maximum %d\n", p->dev_index, p->end.cylinders, part->geo.cylinders - 1); } Part_t *get_part (Partition_t *part, int dev_index) { int i; assert (part != NULL); for (i = 0; i < part->nr_parts; i++) if (part->parts[i].dev_index == dev_index) break; if (i >= part->nr_parts) return NULL; return &part->parts[i]; } /* * Other functions... */ static void move_begin (Partition_t *part, int dev_index) { Part_t *p = get_part (part, dev_index); unsigned long first, new_first; if (!p) return; if (!p->sysid || !p->size || p->sysid == SYSID_EXTENDED) { printf ("Partition %d has no data area\n", dev_index); return; } first = calculate (part, p->begin.heads, p->begin.sectors, p->begin.cylinders); new_first = read_int (part, first, first, p->start + p->size - 1, BASE_LOWER, "New beginning of data"); if (new_first != p->size) { first = p->size + p->start - new_first; p->size = first; p->start = new_first; p->changed = TRUE; } } static void print_raw (Partition_t *part) { printf ("Device: %s\n", part->device); part_dump (part); } static void toggle_active (Partition_t *part, int dev_index) { Part_t *p = get_part (part, dev_index); if (!p) return; if (p->boot_flags) p->boot_flags = 0; else p->boot_flags = ACTIVE_FLAG; p->changed = TRUE; } static void toggle_dos (Partition_t *part) { if ((dos_compatible_flag = !dos_compatible_flag)) sector_offset = part->geo.sectors; else sector_offset = 1; printf ("DOS compatibility flag is %sset", dos_compatible_flag ? "" : "not "); } static int delete_partition (Partition_t *part, int dev_index) { int i, j; assert (part != NULL); for (j = 0; j < part->nr_parts; j++) if (part->parts[j].dev_index == dev_index) break; if (j >= part->nr_parts) return 0; if (part->parts[j].dev_index_owner == part->parts[j].dev_index) { printf ("Cannot delete partition %d\n", dev_index); return 1; } for (i = 0; i < part->nr_parts; i++) if (part->parts[i].dev_index_owner == dev_index && delete_partition (part, part->parts[i].dev_index)) return 1; part->parts[j].changed = TRUE; part->parts[j].boot_flags = 0; part->parts[j].begin.sectors = 0; part->parts[j].begin.heads = 0; part->parts[j].begin.cylinders = 0; part->parts[j].end.sectors = 0; part->parts[j].end.heads = 0; part->parts[j].end.cylinders = 0; part->parts[j].start = 0; part->parts[j].size = 0; part->parts[j].sysid = SYSID_EMPTY; return 0; } static int add_partition (Partition_t *part, int dev_index, int dev_index_owner, PType_t type) { Part_t *p = get_part (part, dev_index); unsigned long *first, *last, start, stop = 0, limit; unsigned long temp; char mesg[48]; int read, i; if (p && p->sysid) { printf ("Partition %d is already defined. Delete " "it before adding it.\n", dev_index); return 0; } first = malloc (sizeof (unsigned long) * part->nr_parts); if (!first) return -1; last = malloc (sizeof (unsigned long) * part->nr_parts); if (!last) return -1; check_bounds (part, first, last); if (unit_flag) for (i = 0; i < part->nr_parts; i++) first[i] = (cround (first[i]) - 1) * display_factor; start = sector_offset; limit = part->geo.heads * part->geo.sectors * part->geo.cylinders - 1; if (type == PT_LOGICAL) { for (i = 0; i < part->nr_parts; i++) if (part->parts[i].dev_index == dev_index_owner) { start = part->parts[i].start + 1; limit = part->parts[i].start + part->parts[i].size - 1; } } sprintf (mesg, "First %s", unit_flag ? "cylinder" : "sector"); read = 0; do { temp = start; for (i = 0; i < part->nr_parts; i++) if (start >= first[i] && start <= last[i] && (part->parts[i].sysid != SYSID_EXTENDED || type != PT_LOGICAL)) start = last[i] + 1; if (start > limit) break; if (start != temp && read) { printf ("Sector %ld is already allocated\n", temp); temp = start = stop; read = 0; } if (!read && start == temp) { unsigned long i; i = stop = start; start = read_int (part, cround (i), cround (i), cround (limit), BASE_IGNORE, mesg); if (unit_flag) { start = (start - 1) * display_factor; if (start < i) start = i; } read = 1; } } while (start != temp || !read); for (i = 0; i < part->nr_parts; i++) { if (start < first[i] && limit >= first[i] && (part->parts[i].sysid != SYSID_EXTENDED || type != PT_LOGICAL)) limit = first[i] - 1; } if (start > limit) { printf ("No free sectors available\n"); return 0; } if (cround (start) == cround (limit)) stop = start; else { sprintf (mesg, "Last %s or +size or +sizeM or +sizeK", unit_flag ? "cylinder" : "sector"); stop = read_int (part, cround (start), cround (limit), cround (limit), BASE_LOWER, mesg); if (unit_flag) { stop = stop * display_factor - 1; if (stop > limit) stop = limit; } } if (!p) { p = realloc (part->parts, sizeof (Part_t) * (part->nr_parts + 1)); if (!p) return -1; part->parts = p; p = &part->parts[part->nr_parts++]; } p->dev_index = dev_index; p->dev_index_owner = dev_index_owner; p->boot_flags = 0; p->sysid = type == PT_EXTENDED ? SYSID_EXTENDED : SYSID_LINUXNAT; p->start = start; p->size = stop - start + 1; p->begin.sectors = start % part->geo.sectors + 1; start /= part->geo.sectors; p->begin.heads = start % part->geo.heads; p->begin.cylinders = start / part->geo.heads; p->end.sectors = stop % part->geo.sectors + 1; stop /= part->geo.sectors; p->end.heads = stop % part->geo.heads; p->end.cylinders = stop / part->geo.heads; p->changed = TRUE; return 0; } static void new_partition (Partition_t *part) { int partition_types, i; PType_t types[3]; partition_types = part_getavailabletypes (part); if (!partition_types) printf ("All partitions are used. Please delete some partitions " "first\n"); else { int lower, upper; i = 0; if (partition_types & 1) types[i++] = PT_PRIMARY; if (partition_types & 2) types[i++] = PT_EXTENDED; if (partition_types & 4) types[i++] = PT_LOGICAL; if (i != 1) { int j; printf ("Command action\n"); for (j = 0; j < i; j++) switch (types[j]) { case PT_PRIMARY: printf (" p primary\n"); break; case PT_EXTENDED: printf (" e extended\n"); break; case PT_LOGICAL: printf (" l logical\n"); break; } i = 0; do { char c; c = tolower (read_char ("\nPartition type: ")); switch (c) { case 'p': if (partition_types & 1) types[i++] = PT_PRIMARY; break; case 'e': if (partition_types & 2) types[i++] = PT_EXTENDED; break; case 'l': if (partition_types & 4) types[i++] = PT_LOGICAL; } } while (!i); } part_getpartrange (part, types[0], &lower, &upper); add_partition (part, select_partition (part, FALSE, lower, upper), 2, types[0]); } } static void change_sysid (Partition_t *part, int dev_index) { Part_t *p = get_part (part, dev_index); int sys; if (!p) return; if (p->sysid == SYSID_EXTENDED) printf ("Partition %d is extended. Delete it\n", dev_index); else while (1) { sys = read_hex (NULL, 0); if (!sys) { delete_partition (part, dev_index); break; } else if (sys == SYSID_EXTENDED) { printf ("You may not change a partition " "to be an extended partition\n"); break; } else if (sys != p->sysid) { p->sysid = sys; printf ("Changed system type of partition %d to %x (%s)\n", dev_index, sys, part_type (p->sysid)); p->changed = TRUE; part->changed = 1; break; } } } static void change_units (Partition_t *part) { if ((unit_flag = !unit_flag)) display_factor = 1; else display_factor = part->geo.heads * part->geo.sectors; update_units (part); printf ("Changing display/entry units to %ss\n", unit_flag ? "cylinder" : "sector"); } static int verify (Partition_t *part) { unsigned int total = 1; unsigned long *first, *last; int i, j; first = malloc (sizeof (unsigned long) * part->nr_parts); if (!first) return -1; last = malloc (sizeof (unsigned long) * part->nr_parts); if (!last) return -1; check_bounds (part, first, last); for (i = 0; i < part->nr_parts; i++) { Part_t *p = &part->parts[i]; if (p->sysid != SYSID_EMPTY) { if (p->sysid != SYSID_EXTENDED) { check_consistency (part, p); if (p->start < first[i]) printf ("Warning: bad start-of-data in partition %d.\n", p->dev_index); check (part, p, last[i]); total += last[i] + 1 - first[i]; if (p->dev_index_owner != p->dev_index) { for (j = 0; j < part->nr_parts; j++) if (p->dev_index_owner == part->parts[j].dev_index) { if (first[i] <= first[j] + 1 || last[i] > last[j]) printf ("Logical partition %d not " "entirely in partition %d\n", p->dev_index, p->dev_index_owner); break; } if (j >= part->nr_parts) printf ("Logical partition %d does not have a valid " "parent\n", p->dev_index); } for (j = 0; j < i; j++) { if (part->parts[j].sysid == SYSID_EXTENDED) continue; if ((first[i] >= first[j] && first[i] <= last[j]) || (last[i] <= last[j] && last[i] >= first[j])) { printf ("Warning: partition %d overlaps partition %d.\n", part->parts[i].dev_index, part->parts[j].dev_index); total += first[i] >= first[j] ? first[i] : first[j]; total -= last[i] <= last[j] ? last[i] : last[j]; } } } else { total += 1; } } } if (total > part->geo.heads * part->geo.sectors * part->geo.cylinders) printf ("Total allocated sectors %d greater than the maximum %d\n", total, part->geo.heads * part->geo.sectors * part->geo.cylinders); else if ((total = part->geo.heads * part->geo.sectors * part->geo.cylinders - total)) printf ("%d unallocated sectors\n", total); return 0; } static bool write_table (Partition_t *part) { return part_write (part) >= 0; } #define REPORT_ERRORS 0 #define IGNORE_ERRORS 1 static void print_part_table (const char *dev, int errorstatus) { static Partition_t *partitions; assert (dev != NULL); if (!errorjump_set && errorstatus == IGNORE_ERRORS) { errorjump_set = TRUE; if (setjmp (errorjump)) { part_close (partitions); partitions = NULL; return; } } partitions = part_open (dev, READ_ONLY); if (!partitions) fatal ("Cannot open %s: %s\n", dev, get_error()); list_table (partitions, LIST_NORMAL); part_close (partitions); partitions = NULL; } static int print_part_size (const char *dev) { Partition_t *partitions; char device[9]; int part_no, i; assert (dev != NULL); if (strlen (dev) <= 8 || (!(part_no = atoi (dev + 8)))) fatal (usage); strncpy (device, dev, 8); device[8] = '\0'; partitions = part_open (device, READ_ONLY); if (!partitions) fatal ("Cannot open %s: %s\n", device, get_error()); for (i = 0; i < partitions->nr_parts; i++) if (partitions->parts[i].dev_index == part_no) { printf ("%ld\n", partitions->parts[i].size / 2); break; } i = i < partitions->nr_parts ? 0 : 1; part_close (partitions); return i; } static void show_extra_menu (void) { puts ("\nCommand action\n" " b move beginning of data in a partition\n" " c change number of cylinders\n" " d print the raw data in the partition table\n" " e list extended partitions\n" " h change number of heads\n" " m print this menu\n" " p print the partition table\n" " q quit without saving changes\n" " r return to main menu\n" " s change number of sectors\n" " v verify the partition table\n" " w write table to disk and exit"); } static bool extra_menu (Partition_t *partitions) { bool quit = FALSE; static bool first = TRUE; if (first) show_extra_menu (); first = FALSE; do { putchar ('\n'); switch (tolower (read_char ("Expert command (m for help): "))) { case 'b': move_begin (partitions, select_partition (partitions, TRUE, 0, 0)); break; case 'c': partitions->geo.cylinders = read_int (partitions, 1, partitions->geo.cylinders, 65535, BASE_DEFAULT, "Number of cylinders"); break; case 'd': print_raw (partitions); break; case 'e': list_table (partitions, LIST_EXTENDED_EXTENDED); break; case 'h': partitions->geo.heads = read_int (partitions, 1, partitions->geo.heads, 256, BASE_DEFAULT, "Number of heads"); update_units (partitions); break; case 'm': default: show_extra_menu (); break; case 'p': list_table (partitions, LIST_EXTENDED_NORMAL); break; case 'q': quit = TRUE; break; case 'r': return FALSE; case 's': partitions->geo.sectors = read_int (partitions, 1, partitions->geo.sectors, 63, BASE_DEFAULT, "Number of sectors"); if (dos_compatible_flag) { sector_offset = partitions->geo.sectors; printf ("Warning: setting sector offset for DOS " "compatability\n"); } update_units (partitions); break; case 'v': verify (partitions); break; case 'w': quit = write_table (partitions); break; } } while (!quit); return TRUE; } static void show_main_menu (void) { puts ("\nCommand action\n" " a toggle a bootable flag\n" " b edit bsd disklabel\n" /* bf */ " c toggle the dos compatiblity flag\n" " d delete a partition\n" " l list known partition types\n" " m print this menu\n" " n add a new partition\n" " p print the partition table\n" " q quit without saving changes\n" " t change a partition's system id\n" " u change display/entry units\n" " v verify the partition table\n" " w write table to disk and exit\n" " x extra functionality (experts only)"); } static void main_menu (const char *dev, part_open_mode_t open_mode) { Partition_t *partitions; bool quit = FALSE; assert (dev != NULL); partitions = part_open (dev, open_mode); if (!partitions) fatal ("Cannot open %s: %s\n", dev, get_error()); if (dos_compatible_flag) sector_offset = partitions->geo.sectors; update_units (partitions); show_main_menu (); do { putchar ('\n'); switch (tolower (read_char ("Command (m for help): "))) { case 'a': toggle_active (partitions, select_partition (partitions, TRUE, 0, 0)); break; case 'b': break; case 'c': toggle_dos (partitions); break; case 'd': delete_partition (partitions, select_partition (partitions, FALSE, 0, 0)); break; case 'l': part_list_types (NULL, 0); break; case 'm': default: show_main_menu (); break; case 'n': new_partition (partitions); break; case 'p': list_table (partitions, LIST_NORMAL); break; case 'q': quit = TRUE; break; case 't': change_sysid (partitions, select_partition (partitions, TRUE, 0, 0)); break; case 'u': change_units (partitions); break; case 'v': verify (partitions); break; case 'w': quit = write_table (partitions); break; case 'x': quit = extra_menu (partitions); break; } } while (!quit); part_close (partitions); } int main (int argc, char *argv[]) { const char *device = NULL; part_open_mode_t open_mode = READ_WRITE; int i; prog_name = strrchr (argv[0], '/'); if (prog_name) prog_name ++; else prog_name = argv[0]; if (argc > 1 && *argv[1] == '-') { switch (argv[1][1]) { case 'v': printf ("fdisk v" VERSION "\n"); exit (0); case 'l': if (argc > 2) { for (i = 2; i < argc; i++) print_part_table (argv[i], REPORT_ERRORS); } else { for (i = 0; i < NR_DEVICES; i++) print_part_table (part_devs[i], IGNORE_ERRORS); } return 0; case 's': if (argc < 3) fatal (usage); return print_part_size (argv[2]); default: fprintf (stderr, "Option -%c not recognised\n", argv[1][1]); exit (1); } } if (argc > 3) fatal (usage); if (argc > 1) { int fd; device = argv[argc - 1]; fd = open (device, O_RDWR); if (fd < 0) { open_mode = READ_ONLY; fd = open (device, O_RDONLY); if (fd < 0) fatal ("can't open device '%s': %s\n", device, strerror (errno)); } close (fd); } else { int fd, mode; for (mode = 0; mode < 2; mode++) { for (i = 0; i < NR_DEF_DEVS; i++) { fd = open (default_devs[i], open_mode == READ_WRITE ? O_RDWR : O_RDONLY); if (fd >= 0) break; } if (fd >= 0) { device = default_devs[i]; close (fd); break; } open_mode = READ_ONLY; } if (!device) fatal ("can't open any device: %s\n", strerror (errno)); printf ("Using %s as default device\n", device); } if (open_mode == READ_ONLY) printf ("Warning: can't get write permission on device '%s'\n", device); main_menu (device, open_mode); exit (0); } #endif