diff options
author | Karel Zak <kzak@redhat.com> | 2014-04-18 11:48:53 +0200 |
---|---|---|
committer | Karel Zak <kzak@redhat.com> | 2014-04-23 11:55:09 +0200 |
commit | 3a6ac12afdfd8d56771f2da49a4cf5fbc054403c (patch) | |
tree | 92b65e724df43a636913994a8d543943ccc06ea5 | |
parent | c79ce9df352af0911d6e4ab19f76c98aa7af9f0c (diff) | |
download | util-linux-3a6ac12afdfd8d56771f2da49a4cf5fbc054403c.tar.gz |
libfdisk: fix logical partition reorder command
Reported-by: Olaf Hering <olaf@aepfle.de>
Signed-off-by: Karel Zak <kzak@redhat.com>
-rw-r--r-- | libfdisk/src/dos.c | 159 |
1 files changed, 109 insertions, 50 deletions
diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c index 18ea8da95..1f5d8b33f 100644 --- a/libfdisk/src/dos.c +++ b/libfdisk/src/dos.c @@ -148,6 +148,17 @@ static sector_t get_abs_partition_start(struct pte *pe) return pe->offset + dos_partition_get_start(pe->pt_entry); } +static sector_t get_abs_partition_end(struct pte *pe) +{ + sector_t size; + + assert(pe); + assert(pe->pt_entry); + + size = dos_partition_get_size(pe->pt_entry); + return get_abs_partition_start(pe) + size - (size ? 1 : 0); +} + /* * Linux kernel cares about partition size only. Things like * partition type or so are completely irrelevant -- kzak Nov-2013 @@ -1723,69 +1734,117 @@ static int dos_list_disklabel(struct fdisk_context *cxt) return rc; } +static void print_chain_of_logicals(struct fdisk_context *cxt) +{ + size_t i; + struct fdisk_dos_label *l = self_label(cxt); + + fputc('\n', stdout); + + for (i = 4; i < cxt->label->nparts_max; i++) { + struct pte *pe = self_pte(cxt, i); + + printf("#%02zu EBR [%10ju], " + "data[start=%10ju (%10ju), size=%10ju], " + "link[start=%10ju (%10ju), size=%10ju]\n", + i, (uintmax_t) pe->offset, + /* data */ + (uintmax_t) dos_partition_get_start(pe->pt_entry), + (uintmax_t) get_abs_partition_start(pe), + (uintmax_t) dos_partition_get_size(pe->pt_entry), + /* link */ + (uintmax_t) dos_partition_get_start(pe->ex_entry), + (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry), + (uintmax_t) dos_partition_get_size(pe->ex_entry)); + } +} + +static int cmp_ebr_offsets(const void *a, const void *b) +{ + struct pte *ae = (struct pte *) a, + *be = (struct pte *) b; + + if (ae->offset == 0 && ae->offset == 0) + return 0; + if (ae->offset == 0) + return 1; + if (be->offset == 0) + return -1; + return ae->offset - be->offset; +} /* * Fix the chain of logicals. - * ext_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. + * The function does not modify data partitions within EBR tables + * (pte->pt_entry). It sorts the chain by EBR offsets and then update links + * (pte->ex_entry) between EBR tables. + * */ static void fix_chain_of_logicals(struct fdisk_context *cxt) { struct fdisk_dos_label *l = self_label(cxt); - size_t j, oj, ojj, sj, sjj; - struct dos_partition *pj,*pjj,tmp; - - /* Stage 1: sort sectors but leave sector of part 4 */ - /* (Its sector is the global ext_offset.) */ -stage1: - for (j = 5; j < cxt->label->nparts_max - 1; j++) { - oj = l->ptes[j].offset; - ojj = l->ptes[j + 1].offset; - if (oj > ojj) { - l->ptes[j].offset = ojj; - l->ptes[j + 1].offset = oj; - pj = l->ptes[j].pt_entry; - dos_partition_set_start(pj, dos_partition_get_start(pj)+oj-ojj); - pjj = l->ptes[j + 1].pt_entry; - dos_partition_set_start(pjj, dos_partition_get_start(pjj)+ojj-oj); - dos_partition_set_start(l->ptes[j - 1].ex_entry, - ojj - l->ext_offset); - dos_partition_set_start(l->ptes[j].ex_entry, - oj - l->ext_offset); - goto stage1; + size_t i; + + DBG(LABEL, print_chain_of_logicals(cxt)); + + /* Sort chain by EBR offsets */ + qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte), + cmp_ebr_offsets); + +again: + /* Sort data partitions by start */ + for (i = 4; i < cxt->label->nparts_max - 1; i++) { + struct pte *cur = self_pte(cxt, i), + *nxt = self_pte(cxt, i + 1); + + if (get_abs_partition_start(cur) > + get_abs_partition_start(nxt)) { + + struct dos_partition tmp = *cur->pt_entry; + sector_t cur_start = get_abs_partition_start(cur), + nxt_start = get_abs_partition_start(nxt); + + /* swap data partitions */ + *cur->pt_entry = *nxt->pt_entry; + *nxt->pt_entry = tmp; + + /* Recount starts according to EBR offsets, the absolute + * address tas to be still the same! */ + dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset); + dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset); + + partition_set_changed(cxt, i, 1); + partition_set_changed(cxt, i + 1, 1); + goto again; } } - /* Stage 2: sort starting sectors */ -stage2: - for (j = 4; j < cxt->label->nparts_max - 1; j++) { - pj = l->ptes[j].pt_entry; - pjj = l->ptes[j + 1].pt_entry; - sj = dos_partition_get_start(pj); - sjj = dos_partition_get_start(pjj); - oj = l->ptes[j].offset; - ojj = l->ptes[j+1].offset; - if (oj+sj > ojj+sjj) { - tmp = *pj; - *pj = *pjj; - *pjj = tmp; - dos_partition_set_start(pj, ojj+sjj-oj); - dos_partition_set_start(pjj, oj+sj-ojj); - goto stage2; + /* Update EBR links */ + for (i = 4; i < cxt->label->nparts_max - 1; i++) { + struct pte *cur = self_pte(cxt, i), + *nxt = self_pte(cxt, i + 1); + + sector_t noff = nxt->offset - l->ext_offset, + ooff = dos_partition_get_start(cur->ex_entry); + + if (noff == ooff) + continue; + + DBG(LABEL, dbgprint("DOS: fix EBR [%10ju] link %ju -> %ju", + (uintmax_t) cur->offset, + (uintmax_t) ooff, (uintmax_t) noff)); + + set_partition(cxt, i, 1, nxt->offset, + get_abs_partition_end(nxt), MBR_DOS_EXTENDED_PARTITION); + + if (i + 1 == cxt->label->nparts_max - 1) { + clear_partition(nxt->ex_entry); + partition_set_changed(cxt, i + 1, 1); } - } - /* Probably something was changed */ - for (j = 4; j < cxt->label->nparts_max; j++) - l->ptes[j].changed = 1; + } + DBG(LABEL, print_chain_of_logicals(cxt)); } int fdisk_dos_fix_order(struct fdisk_context *cxt) |