diff options
Diffstat (limited to 'fdisk/fdisk.c')
-rw-r--r-- | fdisk/fdisk.c | 373 |
1 files changed, 231 insertions, 142 deletions
diff --git a/fdisk/fdisk.c b/fdisk/fdisk.c index cc573646..0126e67b 100644 --- a/fdisk/fdisk.c +++ b/fdisk/fdisk.c @@ -25,6 +25,7 @@ #include "nls.h" #include "blkdev.h" #include "common.h" +#include "mbsalign.h" #include "fdisk.h" #include "wholedisk.h" @@ -222,7 +223,12 @@ unsigned int heads, display_in_cyl_units = 1; unsigned long long total_number_of_sectors; /* (!) 512-byte sectors */ -unsigned long io_size, alignment_offset; +unsigned long grain = DEFAULT_SECTOR_SIZE, + io_size = DEFAULT_SECTOR_SIZE, + min_io_size = DEFAULT_SECTOR_SIZE, + phy_sector_size = DEFAULT_SECTOR_SIZE, + alignment_offset; +int has_topology; #define dos_label (!sun_label && !sgi_label && !aix_label && !mac_label && !osf_label) int sun_label = 0; /* looking at sun disklabel */ @@ -237,6 +243,7 @@ jmp_buf listingbuf; void fatal(enum failure why) { char error[LINE_LENGTH], *message = error; + int rc = EXIT_FAILURE; if (listing) { close(fd); @@ -244,25 +251,23 @@ void fatal(enum failure why) { } switch (why) { + case help: + rc = EXIT_SUCCESS; case usage: message = _( -"Usage: fdisk [-b SSZ] [-u] DISK Change partition table\n" -" fdisk -l [-b SSZ] [-u] DISK List partition table(s)\n" -" fdisk -s PARTITION Give partition size(s) in blocks\n" -" fdisk -v Give fdisk version\n" -"Here DISK is something like /dev/hdb or /dev/sda\n" -"and PARTITION is something like /dev/hda7\n" -"-u: give Start and End in sector (instead of cylinder) units\n" -"-b 2048: (for certain MO disks) use 2048-byte sectors\n"); - break; - case usage2: - /* msg in cases where fdisk used to probe */ - message = _( -"Usage: fdisk [-l] [-b SSZ] [-u] device\n" -"E.g.: fdisk /dev/hda (for the first IDE disk)\n" -" or: fdisk /dev/sdc (for the third SCSI disk)\n" -" or: fdisk /dev/eda (for the first PS/2 ESDI drive)\n" -" or: fdisk /dev/rd/c0d0 or: fdisk /dev/ida/c0d0 (for RAID devices)\n" -" ...\n"); +"Usage:\n" +" fdisk [options] <disk> change partition table\n" +" fdisk [options] -l <disk> list partition table(s)\n" +" fdisk -s <partition> give partition size(s) in blocks\n" +"\nOptions:\n" +" -b <size> sector size (512, 1024, 2048 or 4096)\n" +" -c switch off DOS-compatible mode\n" +" -h print help\n" +" -u <size> give sizes in sectors instead of cylinders\n" +" -v print 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"); break; case unable_to_open: snprintf(error, sizeof(error), @@ -294,7 +299,7 @@ void fatal(enum failure why) { fputc('\n', stderr); fputs(message, stderr); - exit(1); + exit(rc); } static void @@ -564,9 +569,19 @@ void list_types(struct systypes *sys) i = done = 0; do { - printf("%c%2x %-15.15s", i ? ' ' : '\n', - sys[next].type, _(sys[next].name)); - next = last[i++] + done; + #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; @@ -637,17 +652,15 @@ test_c(char **m, char *mesg) { return val; } -#define alignment_required (io_size != sector_size) +#define alignment_required (grain != sector_size) static int lba_is_aligned(unsigned long long lba) { - unsigned long long bytes, phy_sectors; + unsigned int granularity = max(phy_sector_size, min_io_size); + unsigned long long offset = (lba << 9) & (granularity - 1); - bytes = lba * sector_size; - phy_sectors = bytes / io_size; - - return (alignment_offset + (phy_sectors * io_size) == bytes); + return !((granularity + alignment_offset - offset) & (granularity - 1)); } #define ALIGN_UP 1 @@ -657,39 +670,52 @@ lba_is_aligned(unsigned long long lba) static unsigned long long align_lba(unsigned long long lba, int direction) { - unsigned long long sects_in_phy, res; + unsigned long long res; if (lba_is_aligned(lba)) - return lba; + res = lba; + else { + unsigned long long sects_in_phy = grain / sector_size; - sects_in_phy = io_size / sector_size; + if (lba < sector_offset) + res = sector_offset; - if (lba < sects_in_phy) - /* align to the first physical sector */ - res = sects_in_phy; + else if (direction == ALIGN_UP) + res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy; - 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 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; - else /* ALIGN_NEAREST */ - res = ((lba + sects_in_phy/2) / sects_in_phy) * sects_in_phy; + if (alignment_offset && !lba_is_aligned(res) && + res > alignment_offset / 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(phy_sector_size, min_io_size) - + alignment_offset) / sector_size; - if (alignment_offset) - /* - * apply alignment_offset - * - * On disk with alignment compensation physical blocks start - * at LBA < 0 (usually LBA -1). It means we have to move LBA - * according the offset to be on the physical boundary. - */ - res -= (io_size - alignment_offset) / sector_size; + if (direction == ALIGN_UP && res < lba) + res += sects_in_phy; + } + } - /* fprintf(stderr, "LBA %llu -align-> %llu (%s)\n", lba, res, - * lba_is_aligned(res) ? "OK" : "FALSE"); - */ + /*** + 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; } @@ -742,19 +768,6 @@ void update_units(void) } static void -warn_cylinders(void) { - if (dos_label && cylinders > 1024 && !nowarn) - fprintf(stderr, _("\n" -"The number of cylinders for this disk is set to %d.\n" -"There is nothing wrong with that, but this is larger than 1024,\n" -"and could in certain setups cause problems with:\n" -"1) software that runs at boot time (e.g., old versions of LILO)\n" -"2) booting and partitioning software from other OSs\n" -" (e.g., DOS FDISK, OS/2 FDISK)\n"), - cylinders); -} - -static void warn_limits(void) { if (total_number_of_sectors > UINT_MAX && !nowarn) { int giga = (total_number_of_sectors << 9) / 1000000000; @@ -774,28 +787,30 @@ warn_limits(void) { static void warn_alignment(void) { - if (nowarn || !alignment_required) + if (nowarn) return; - fprintf(stderr, _("\n" + if (sector_size != phy_sector_size) + fprintf(stderr, _("\n" "The device presents a logical sector size that is smaller than\n" -"the optimal I/O size (often physical sector size). Aligning to a optimal\n" -"I/O size boundary is recommended, or performance may be impacted.\n\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")); - /* - * Print warning when sector_offset is not aligned for DOS mode - */ - if (alignment_offset == 0 && dos_compatible_flag && - !lba_is_aligned(sector_offset)) + if (dos_compatible_flag) { + fprintf(stderr, _("\n" +"WARNING: DOS-compatible mode is deprecated. It's strongly recommended to\n" +" switch off the mode (command 'c')")); - fprintf(stderr, _( -"WARNING: The device does not provide compensation (alignment_offset)\n" -"for DOS-compatible partitioning, but DOS-compatible mode is enabled.\n" -"Use command 'c' to switch-off DOS mode.\n\n")); + if (display_in_cyl_units) + fprintf(stderr, _(" and change display units to\n" +" sectors (command 'u').\n")); + else + fprintf(stderr, ".\n"); - if (display_in_cyl_units) - fprintf(stderr, _( -"It's recommended to change display units to sectors (command 'u').\n\n")); + } else 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")); } @@ -961,29 +976,52 @@ get_topology(int fd) { #ifdef HAVE_LIBBLKID_INTERNAL blkid_probe pr; - if (user_set_sector_size) - return; pr = blkid_new_probe(); if (pr && blkid_probe_set_device(pr, fd, 0, 0) == 0) { blkid_topology tp = blkid_probe_get_topology(pr); if (tp) { + min_io_size = blkid_topology_get_minimum_io_size(tp); io_size = blkid_topology_get_optimal_io_size(tp); - if (!io_size) - io_size = blkid_topology_get_minimum_io_size(tp); + phy_sector_size = blkid_topology_get_physical_sector_size(tp); alignment_offset = blkid_topology_get_alignment_offset(tp); + + /* We assume that the device provides topology info if + * optimal_io_size is set or alignment_offset is set or + * minimum_io_size is not power of 2. + * + * See also update_sector_offset(). + */ + if (io_size || alignment_offset || + (min_io_size & (min_io_size - 1))) + has_topology = 1; + if (!io_size) + /* optimal IO is optional, default to minimum IO */ + io_size = min_io_size; } } blkid_free_probe(pr); #endif if (user_set_sector_size) - ; - else if (blkdev_get_sector_size(fd, &arg) == 0) + /* fdisk since 2.17 differentiate between logical and physical + * sectors size. For backward compatibility the + * fdisk -b <sectorsize> + * changes both, logical and physical sector size. + */ + phy_sector_size = sector_size; + + else if (blkdev_get_sector_size(fd, &arg) == 0) { sector_size = arg; + if (!phy_sector_size) + phy_sector_size = sector_size; + } + + if (!min_io_size) + min_io_size = phy_sector_size; if (!io_size) - io_size = sector_size; + io_size = min_io_size; if (sector_size != DEFAULT_SECTOR_SIZE) printf(_("Note: sector size is %d (not %d)\n"), @@ -1041,24 +1079,47 @@ get_partition_table_geometry(void) { void update_sector_offset(void) { - if (dos_compatible_flag) { - /* usually 63 sectors for classic geometry */ - sector_offset = sectors; + grain = io_size; - /* On the disks with alignment_offset the default geo.sectors - * has to be aligned to physical block boundary. Check it! - */ - if (sectors && alignment_offset && !lba_is_aligned(sectors)) - fprintf(stderr, _( - "\nWARNING: the device provides alignment_offset, but the offset does not\n" - "match with device geometry.\n\n")); - } else { + if (dos_compatible_flag) + sector_offset = sectors; /* usually 63 sectors */ + else { /* - * Align the begin of the first partition to the physical block + * 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 */ - unsigned long long x = io_size / sector_size; + unsigned long long x = 0; + + if (has_topology) { + if (alignment_offset) + x = alignment_offset; + else if (io_size > 2048 * 512) + x = io_size; + } + /* default to 1MiB */ + if (!x) + x = 2048 * 512; + + sector_offset = x / sector_size; + + /* don't use huge offset on small devices */ + if (total_number_of_sectors <= sector_offset * 4) + sector_offset = phy_sector_size / sector_size; - sector_offset = align_lba(x, ALIGN_UP); + /* use 1MiB grain always when possible */ + if (grain < 2048 * 512) + grain = 2048 * 512; + + /* don't use huge grain on small devices */ + if (total_number_of_sectors <= (grain * 4 / sector_size)) + grain = phy_sector_size; } } @@ -1249,7 +1310,6 @@ got_dos_table: } } - warn_cylinders(); warn_geometry(); warn_limits(); warn_alignment(); @@ -1462,11 +1522,11 @@ read_int(unsigned int low, unsigned int dflt, unsigned int high, int -get_partition(int warn, int max) { +get_partition_dflt(int warn, int max, int dflt) { struct pte *pe; int i; - i = read_int(1, 0, max, 0, _("Partition number")) - 1; + i = read_int(1, dflt, max, 0, _("Partition number")) - 1; pe = &ptes[i]; if (warn) { @@ -1483,6 +1543,11 @@ get_partition(int warn, int max) { return i; } +int +get_partition(int warn, int max) { + return get_partition_dflt(warn, max, 0); +} + static int get_existing_partition(int warn, int max) { int pno = -1; @@ -1513,14 +1578,17 @@ static int get_nonexisting_partition(int warn, int max) { int pno = -1; int i; + int dflt = 0; 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) + if (pno >= 0) { + dflt = pno + 1; goto not_unique; + } pno = i; } } @@ -1532,7 +1600,7 @@ get_nonexisting_partition(int warn, int max) { return -1; not_unique: - return get_partition(warn, max); + return get_partition_dflt(warn, max, dflt); } const char * @@ -1751,6 +1819,9 @@ static void check_consistency(struct partition *p, int partition) { 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 (!heads || !sectors || (partition >= 4)) return; /* do not check extended partitions */ @@ -1809,10 +1880,10 @@ static void check_consistency(struct partition *p, int partition) { } static void -check_alignment(struct partition *p, int partition) +check_alignment(unsigned long long lba, int partition) { - if (!lba_is_aligned(get_start_sect(p))) - printf(_("Partition %i does not start on optimal I/O size boundary.\n"), + if (!lba_is_aligned(lba)) + printf(_("Partition %i does not start on physical sector boundary.\n"), partition + 1); } @@ -1839,8 +1910,10 @@ list_disk_geometry(void) { str_units(PLURAL), units_per_sector, sector_size, units_per_sector * sector_size); - printf(_("Sector size (logical / optimal IO): %u bytes / %lu bytes\n"), - sector_size, io_size); + printf(_("Sector size (logical/physical): %u bytes / %lu bytes\n"), + sector_size, phy_sector_size); + printf(_("I/O size (minimum/optimal): %lu bytes / %lu bytes\n"), + min_io_size, io_size); if (alignment_offset) printf(_("Alignment offset: %lu bytes\n"), alignment_offset); if (dos_label) @@ -2051,7 +2124,7 @@ list_table(int xtra) { /* type name */ (type = partition_type(p->sys_ind)) ? type : _("Unknown")); check_consistency(p, i); - check_alignment(p, i); + check_alignment(get_partition_start(pe), i); } } @@ -2086,7 +2159,7 @@ x_list_table(int extend) { (unsigned long) get_nr_sects(p), p->sys_ind); if (p->sys_ind) { check_consistency(p, i); - check_alignment(p, i); + check_alignment(get_partition_start(pe), i); } } } @@ -2164,7 +2237,7 @@ verify(void) { p = pe->part_table; if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) { check_consistency(p, i); - check_alignment(p, i); + check_alignment(get_partition_start(pe), i); if (get_partition_start(pe) < first[i]) printf(_("Warning: bad start-of-data in " "partition %d\n"), i + 1); @@ -2273,17 +2346,16 @@ add_partition(int n, int sys) { snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); do { - unsigned long long dflt, dflt_tmp; + unsigned long long dflt, aligned; temp = start; - dflt = start = get_unused_start(n, start, first, last); /* the default sector should be aligned and unused */ do { - dflt_tmp = align_lba_in_range(dflt, start, limit); - dflt = get_unused_start(n, dflt_tmp, first, last); - } while (dflt != dflt_tmp && dflt > dflt_tmp && dflt < limit); + aligned = align_lba_in_range(dflt, dflt, limit); + dflt = get_unused_start(n, aligned, first, last); + } while (dflt != aligned && dflt > aligned && dflt < limit); if (dflt >= limit) dflt = start; @@ -2590,7 +2662,8 @@ static void move_begin(int i) { struct pte *pe = &ptes[i]; struct partition *p = pe->part_table; - unsigned int new, first; + unsigned int new, free_start, curr_start, last; + int x; if (warn_geometry()) return; @@ -2598,13 +2671,37 @@ move_begin(int i) { printf(_("Partition %d has no data area\n"), i + 1); return; } - first = get_partition_start(pe); - new = read_int(first, first, first + get_nr_sects(p) - 1, first, + + /* 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(free_start, curr_start, last, free_start, _("New beginning of data")) - pe->offset; if (new != get_nr_sects(p)) { - first = get_nr_sects(p) + get_start_sect(p) - new; - set_nr_sects(p, first); + 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; } @@ -2632,8 +2729,6 @@ xselect(void) { _("Number of cylinders")); if (sun_label) sun_set_ncyl(cylinders); - if (dos_label) - warn_cylinders(); break; case 'd': print_raw(); @@ -2830,17 +2925,7 @@ main(int argc, char **argv) { bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - /* - * Calls: - * fdisk -v - * fdisk -l [-b sectorsize] [-u] device ... - * fdisk -s [partition] ... - * fdisk [-b sectorsize] [-u] device - * - * Options -C, -H, -S set the geometry. - * - */ - while ((c = getopt(argc, argv, "b:C:H:lsS:uvV")) != -1) { + while ((c = getopt(argc, argv, "b:cC:hH:lsS:uvV")) != -1) { switch (c) { case 'b': /* Ugly: this sector size is really per device, @@ -2857,6 +2942,12 @@ main(int argc, char **argv) { case 'C': user_cylinders = atoi(optarg); break; + case 'c': + dos_compatible_flag = 0; + break; + case 'h': + fatal(help); + break; case 'H': user_heads = atoi(optarg); if (user_heads <= 0 || user_heads > 256) @@ -2942,10 +3033,8 @@ main(int argc, char **argv) { if (argc-optind == 1) disk_device = argv[optind]; - else if (argc-optind != 0) - fatal(usage); else - fatal(usage2); + fatal(usage); gpt_warning(disk_device); get_boot(fdisk); |