From: Pali Rohár Date: Fri, 30 Jul 2021 09:15:15 +0000 (+0200) Subject: libfdisk: (dos) Add function fdisk_dos_fix_chs() for fixing CHS values for all partitions X-Git-Tag: v2.38-rc1~327^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dac364addbfb32b099658c89fcc5e40c834ccf79;p=thirdparty%2Futil-linux.git libfdisk: (dos) Add function fdisk_dos_fix_chs() for fixing CHS values for all partitions This function fixes beginning and ending CHS values for every partition according to LBA relative offset, relative beginning and size and fdisk idea of disk geometry (sectors per track and number of heads). This function may be used for repairing existing partitions to be compatible with CHS addressing. Signed-off-by: Pali Rohár --- diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt index 4894c3633b..1589413b86 100644 --- a/libfdisk/docs/libfdisk-sections.txt +++ b/libfdisk/docs/libfdisk-sections.txt @@ -220,6 +220,7 @@ DOS_FLAG_ACTIVE fdisk_dos_enable_compatible fdisk_dos_is_compatible fdisk_dos_move_begin +fdisk_dos_fix_chs
diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c index 0ace22666f..e72b644f2e 100644 --- a/libfdisk/src/dos.c +++ b/libfdisk/src/dos.c @@ -780,10 +780,10 @@ static inline int chs_overflowed(unsigned int c, unsigned int h, unsigned int s) return (c == 1023 && (h == 254 || h == 255) && s == 63); } -static inline int lba_overflowed(unsigned int start, unsigned int sects) +static inline int lba_overflowed(fdisk_sector_t start, fdisk_sector_t sects) { - /* Check if the last LBA sector can be represented by unsigned int */ - return (start + (sects-1) < start); + /* Check if the last LBA sector can be represented by unsigned 32bit int */ + return (start + (sects-1) > UINT32_MAX); } static void get_partition_table_geometry(struct fdisk_context *cxt, @@ -2586,6 +2586,87 @@ static int dos_reorder(struct fdisk_context *cxt) return 0; } +/** + * fdisk_dos_fix_chs: + * @cxt: fdisk context + * + * Fix beginning and ending C/H/S values for every partition + * according to LBA relative offset, relative beginning and + * size and fdisk idea of disk geometry (sectors per track + * and number of heads). + * + * Returns: number of fixed (changed) partitions. + */ +int fdisk_dos_fix_chs(struct fdisk_context *cxt) +{ + unsigned int obc, obh, obs; /* old beginning c, h, s */ + unsigned int oec, oeh, oes; /* old ending c, h, s */ + unsigned int nbc, nbh, nbs; /* new beginning c, h, s */ + unsigned int nec, neh, nes; /* new ending c, h, s */ + fdisk_sector_t l, sects; /* lba beginning and size */ + struct dos_partition *p; + struct pte *pe; + int changed = 0; + size_t i; + + assert(fdisk_is_label(cxt, DOS)); + + for (i = 0; i < cxt->label->nparts_max; i++) { + p = self_partition(cxt, i); + if (!p || !is_used_partition(p)) + continue; + + pe = self_pte(cxt, i); + + /* old beginning c, h, s */ + obc = cylinder(p->bs, p->bc); + obh = p->bh; + obs = sector(p->bs); + + /* old ending c, h, s */ + oec = cylinder(p->es, p->ec); + oeh = p->eh; + oes = sector(p->es); + + /* new beginning c, h, s */ + l = get_abs_partition_start(pe); + long2chs(cxt, l, &nbc, &nbh, &nbs); + if (l > UINT32_MAX || nbc >= 1024) { + nbc = 1023; + nbh = cxt->geom.heads-1; + nbs = cxt->geom.sectors; + } + + /* new ending c, h, s */ + sects = dos_partition_get_size(p); + long2chs(cxt, l + sects - 1, &nec, &neh, &nes); + if (lba_overflowed(l, sects) || nec >= 1024) { + nec = 1023; + neh = cxt->geom.heads-1; + nes = cxt->geom.sectors; + } + + if (obc != nbc || obh != nbh || obs != nbs || + oec != nec || oeh != neh || oes != nes) { + DBG(LABEL, ul_debug("DOS: changing %zu partition CHS " + "from (%d, %d, %d)-(%d, %d, %d) " + "to (%d, %d, %d)-(%d, %d, %d)", + i+1, obc, obh, obs, oec, oeh, oes, + nbc, nbh, nbs, nec, neh, nes)); + p->bc = nbc & 0xff; + p->bh = nbh; + p->bs = nbs | ((nbc >> 2) & 0xc0); + p->ec = nec & 0xff; + p->eh = neh; + p->es = nes | ((nec >> 2) & 0xc0); + partition_set_changed(cxt, i, 1); + changed++; + } + } + + return changed; +} + /* TODO: use fdisk_set_partition() API */ int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i) { diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in index 238aa1304b..6d1262a55b 100644 --- a/libfdisk/src/libfdisk.h.in +++ b/libfdisk/src/libfdisk.h.in @@ -619,6 +619,7 @@ extern int fdisk_iter_get_direction(struct fdisk_iter *itr); /* dos.c */ #define DOS_FLAG_ACTIVE 1 +extern int fdisk_dos_fix_chs(struct fdisk_context *cxt); extern int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i); extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable); extern int fdisk_dos_is_compatible(struct fdisk_label *lb); diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym index 3e30310571..71de80589c 100644 --- a/libfdisk/src/libfdisk.sym +++ b/libfdisk/src/libfdisk.sym @@ -316,3 +316,7 @@ FDISK_2.36 { fdisk_label_advparse_parttype; fdisk_label_get_parttype_shortcut; } FDISK_2.35; + +FDISK_2.38 { + fdisk_dos_fix_chs; +} FDISK_2.36;