Home | History | Annotate | Download | only in chain
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
      5  *   Copyright 2010 Shao Miller
      6  *   Copyright 2010-2012 Michal Soltys
      7  *
      8  *   Permission is hereby granted, free of charge, to any person
      9  *   obtaining a copy of this software and associated documentation
     10  *   files (the "Software"), to deal in the Software without
     11  *   restriction, including without limitation the rights to use,
     12  *   copy, modify, merge, publish, distribute, sublicense, and/or
     13  *   sell copies of the Software, and to permit persons to whom
     14  *   the Software is furnished to do so, subject to the following
     15  *   conditions:
     16  *
     17  *   The above copyright notice and this permission notice shall
     18  *   be included in all copies or substantial portions of the Software.
     19  *
     20  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     21  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     22  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     23  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     24  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     25  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     26  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     27  *   OTHER DEALINGS IN THE SOFTWARE.
     28  *
     29  * ----------------------------------------------------------------------- */
     30 
     31 #include <com32.h>
     32 #include <stdlib.h>
     33 #include <stdio.h>
     34 #include <string.h>
     35 #include <stdint.h>
     36 #include <dprintf.h>
     37 #include <syslinux/config.h>
     38 #include "chain.h"
     39 #include "options.h"
     40 #include "utility.h"
     41 #include "partiter.h"
     42 #include "mangle.h"
     43 
     44 static const char cmldr_signature[8] = "cmdcons";
     45 
     46 /* Create boot info table: needed when you want to chainload
     47  * another version of ISOLINUX (or another bootlaoder that needs
     48  * the -boot-info-table switch of mkisofs)
     49  * (will only work when run from ISOLINUX)
     50  */
     51 int manglef_isolinux(struct data_area *data)
     52 {
     53     const union syslinux_derivative_info *sdi;
     54     unsigned char *isolinux_bin;
     55     uint32_t *checksum, *chkhead, *chktail;
     56     uint32_t file_lba = 0;
     57 
     58     if (!(opt.file && opt.isolinux))
     59 	return 0;
     60 
     61     sdi = syslinux_derivative_info();
     62 
     63     if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
     64 	error("The isolinux= option is only valid when run from ISOLINUX.");
     65 	goto bail;
     66     }
     67 
     68     /* Boot info table info (integers in little endian format)
     69 
     70        Offset Name         Size      Meaning
     71        8      bi_pvd       4 bytes   LBA of primary volume descriptor
     72        12     bi_file      4 bytes   LBA of boot file
     73        16     bi_length    4 bytes   Boot file length in bytes
     74        20     bi_csum      4 bytes   32-bit checksum
     75        24     bi_reserved  40 bytes  Reserved
     76 
     77        The 32-bit checksum is the sum of all the 32-bit words in the
     78        boot file starting at byte offset 64. All linear block
     79        addresses (LBAs) are given in CD sectors (normally 2048 bytes).
     80 
     81        LBA of primary volume descriptor should already be set to 16.
     82        */
     83 
     84     isolinux_bin = (unsigned char *)data->data;
     85 
     86     /* Get LBA address of bootfile */
     87     file_lba = get_file_lba(opt.file);
     88 
     89     if (file_lba == 0) {
     90 	error("Failed to find LBA offset of the boot file.");
     91 	goto bail;
     92     }
     93     /* Set it */
     94     *((uint32_t *) & isolinux_bin[12]) = file_lba;
     95 
     96     /* Set boot file length */
     97     *((uint32_t *) & isolinux_bin[16]) = data->size;
     98 
     99     /* Calculate checksum */
    100     checksum = (uint32_t *) & isolinux_bin[20];
    101     chkhead = (uint32_t *) & isolinux_bin[64];
    102     chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
    103     *checksum = 0;
    104     while (chkhead < chktail)
    105 	*checksum += *chkhead++;
    106 
    107     /*
    108      * Deal with possible fractional dword at the end;
    109      * this *should* never happen...
    110      */
    111     if (data->size & 3) {
    112 	uint32_t xword = 0;
    113 	memcpy(&xword, chkhead, data->size & 3);
    114 	*checksum += xword;
    115     }
    116     return 0;
    117 bail:
    118     return -1;
    119 }
    120 
    121 /*
    122  * Legacy grub's stage2 chainloading
    123  */
    124 int manglef_grub(const struct part_iter *iter, struct data_area *data)
    125 {
    126     /* Layout of stage2 file (from byte 0x0 to 0x270) */
    127     struct grub_stage2_patch_area {
    128 	/* 0x0 to 0x205 */
    129 	char unknown[0x206];
    130 	/* 0x206: compatibility version number major */
    131 	uint8_t compat_version_major;
    132 	/* 0x207: compatibility version number minor */
    133 	uint8_t compat_version_minor;
    134 
    135 	/* 0x208: install_partition variable */
    136 	struct {
    137 	    /* 0x208: sub-partition in sub-partition part2 */
    138 	    uint8_t part3;
    139 	    /* 0x209: sub-partition in top-level partition */
    140 	    uint8_t part2;
    141 	    /* 0x20a: top-level partiton number */
    142 	    uint8_t part1;
    143 	    /* 0x20b: BIOS drive number (must be 0) */
    144 	    uint8_t drive;
    145 	} __attribute__ ((packed)) install_partition;
    146 
    147 	/* 0x20c: deprecated (historical reason only) */
    148 	uint32_t saved_entryno;
    149 	/* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
    150 	uint8_t stage2_id;
    151 	/* 0x211: force LBA */
    152 	uint8_t force_lba;
    153 	/* 0x212: version string (will probably be 0.97) */
    154 	char version_string[5];
    155 	/* 0x217: config filename */
    156 	char config_file[89];
    157 	/* 0x270: start of code (after jump from 0x200) */
    158 	char codestart[1];
    159     } __attribute__ ((packed)) *stage2;
    160 
    161     if (!(opt.file && opt.grub))
    162 	return 0;
    163 
    164     if (data->size < sizeof *stage2) {
    165 	error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.");
    166 	goto bail;
    167     }
    168     stage2 = data->data;
    169 
    170     /*
    171      * Check the compatibility version number to see if we loaded a real
    172      * stage2 file or a stage2 file that we support.
    173      */
    174     if (stage2->compat_version_major != 3
    175 	    || stage2->compat_version_minor != 2) {
    176 	error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.");
    177 	goto bail;
    178     }
    179 
    180     /*
    181      * GRUB Legacy wants the partition number in the install_partition
    182      * variable, located at offset 0x208 of stage2.
    183      * When GRUB Legacy is loaded, it is located at memory address 0x8208.
    184      *
    185      * It looks very similar to the "boot information format" of the
    186      * Multiboot specification:
    187      *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
    188      *
    189      *   0x208 = part3: sub-partition in sub-partition part2
    190      *   0x209 = part2: sub-partition in top-level partition
    191      *   0x20a = part1: top-level partition number
    192      *   0x20b = drive: BIOS drive number (must be 0)
    193      *
    194      * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
    195      * another location.
    196      *
    197      * Partition numbers always start from zero.
    198      * Unused partition bytes must be set to 0xFF.
    199      *
    200      * We only care about top-level partition, so we only need to change
    201      * "part1" to the appropriate value:
    202      *   -1:   whole drive (default) (-1 = 0xFF)
    203      *   0-3:  primary partitions
    204      *   4-*:  logical partitions
    205      */
    206     stage2->install_partition.part1 = iter->index - 1;
    207 
    208     /*
    209      * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
    210      * config filename. The filename passed via grubcfg= will overwrite
    211      * the default config filename "/boot/grub/menu.lst".
    212      */
    213     if (opt.grubcfg) {
    214 	if (strlen(opt.grubcfg) >= sizeof stage2->config_file) {
    215 	    error("The config filename length can't exceed 88 characters.");
    216 	    goto bail;
    217 	}
    218 
    219 	strcpy((char *)stage2->config_file, opt.grubcfg);
    220     }
    221 
    222     return 0;
    223 bail:
    224     return -1;
    225 }
    226 #if 0
    227 /*
    228  * Dell's DRMK chainloading.
    229  */
    230 int manglef_drmk(struct data_area *data)
    231 {
    232     /*
    233      * DRMK entry is different than MS-DOS/PC-DOS
    234      * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
    235      * We only really need 4 new, usable bytes at the end.
    236      */
    237 
    238     if (!(opt.file && opt.drmk))
    239 	return 0;
    240 
    241     uint32_t tsize = (data->size + 19) & 0xfffffff0;
    242     const union syslinux_derivative_info *sdi;
    243     uint64_t fs_lba;
    244 
    245     sdi = syslinux_derivative_info();
    246     /* We should lookup the Syslinux partition offset and use it */
    247     fs_lba = *sdi->disk.partoffset;
    248 
    249     /*
    250      * fs_lba should be verified against the disk as some DRMK
    251      * variants will check and fail if it does not match
    252      */
    253     dprintf("  fs_lba offset is %d\n", fs_lba);
    254     /* DRMK only uses a DWORD */
    255     if (fs_lba > 0xffffffff) {
    256 	error("LBA very large; Only using lower 32 bits; DRMK will probably fail.");
    257     }
    258     opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;	/* Used before initialized */
    259     if (!realloc(data->data, tsize)) {
    260 	error("Failed to realloc for DRMK.");
    261 	goto bail;
    262     }
    263     data->size = tsize;
    264     /* ds:bp is assumed by DRMK to be the boot sector */
    265     /* offset 28 is the FAT HiddenSectors value */
    266     opt.regs.ds = (tsize >> 4) + (opt.fseg - 2);
    267     /* "Patch" into tail of the new space */
    268     *(uint32_t *)((char*)data->data + tsize - 4) = fs_lba;
    269 
    270     return 0;
    271 bail:
    272     return -1;
    273 }
    274 #endif
    275 /* Adjust BPB common function */
    276 static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag)
    277 {
    278     int type = bpb_detect(data->data, tag);
    279     int off = drvoff_detect(type);
    280 
    281     /* BPB: hidden sectors 64bit - exFAT only for now */
    282     if (type == bpbEXF)
    283 	    *(uint64_t *) ((char *)data->data + 0x40) = iter->abs_lba;
    284     /* BPB: hidden sectors 32bit*/
    285     else if (bpbV34 <= type && type <= bpbV70) {
    286 	if (iter->abs_lba < ~0u)
    287 	    *(uint32_t *) ((char *)data->data + 0x1c) = iter->abs_lba;
    288 	else
    289 	    /* won't really help much, but ... */
    290 	    *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
    291     /* BPB: hidden sectors 16bit*/
    292     } else if (bpbV30 <= type && type <= bpbV32) {
    293 	if (iter->abs_lba < 0xFFFF)
    294 	    *(uint16_t *) ((char *)data->data + 0x1c) = iter->abs_lba;
    295 	else
    296 	    /* won't really help much, but ... */
    297 	    *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
    298     }
    299 
    300     /* BPB: legacy geometry */
    301     if (bpbV30 <= type && type <= bpbV70) {
    302 	if (iter->di.cbios)
    303 	    *(uint32_t *)((char *)data->data + 0x18) = (iter->di.head << 16) | iter->di.spt;
    304 	else {
    305 	    if (iter->di.disk & 0x80)
    306 		*(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
    307 	    else
    308 		*(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
    309 	}
    310     }
    311     /* BPB: drive */
    312     if (off >= 0) {
    313 	*(uint8_t *)((char *)data->data + off) = (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
    314     }
    315 
    316     return 0;
    317 }
    318 
    319 /*
    320  * Adjust BPB of a BPB-compatible file
    321  */
    322 int manglef_bpb(const struct part_iter *iter, struct data_area *data)
    323 {
    324     if (!(opt.file && opt.filebpb))
    325 	return 0;
    326 
    327     return mangle_bpb(iter, data, "file");
    328 }
    329 
    330 /*
    331  * Adjust BPB of a sector
    332  */
    333 int mangles_bpb(const struct part_iter *iter, struct data_area *data)
    334 {
    335     if (!(opt.sect && opt.setbpb))
    336 	return 0;
    337 
    338     return mangle_bpb(iter, data, "sect");
    339 }
    340 
    341 /*
    342  * This function performs full BPB patching, analogously to syslinux's
    343  * native BSS.
    344  */
    345 int manglesf_bss(struct data_area *sec, struct data_area *fil)
    346 {
    347     int type1, type2;
    348     size_t cnt = 0;
    349 
    350     if (!(opt.sect && opt.file && opt.bss))
    351 	return 0;
    352 
    353     type1 = bpb_detect(fil->data, "bss/file");
    354     type2 = bpb_detect(sec->data, "bss/sect");
    355 
    356     if (!type1 || !type2) {
    357 	error("Couldn't determine the BPB type for option 'bss'.");
    358 	goto bail;
    359     }
    360     if (type1 != type2) {
    361 	error("Option 'bss' can't be used,\n"
    362 		"when a sector and a file have incompatible BPBs.");
    363 	goto bail;
    364     }
    365 
    366     /* Copy common 2.0 data */
    367     memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
    368 
    369     /* Copy 3.0+ data */
    370     if (type1 <= bpbV30) {
    371 	cnt = 0x06;
    372     } else if (type1 <= bpbV32) {
    373 	cnt = 0x08;
    374     } else if (type1 <= bpbV34) {
    375 	cnt = 0x0C;
    376     } else if (type1 <= bpbV40) {
    377 	cnt = 0x2E;
    378     } else if (type1 <= bpbVNT) {
    379 	cnt = 0x3C;
    380     } else if (type1 <= bpbV70) {
    381 	cnt = 0x42;
    382     } else if (type1 <= bpbEXF) {
    383 	cnt = 0x60;
    384     }
    385     memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
    386 
    387     return 0;
    388 bail:
    389     return -1;
    390 }
    391 
    392 /*
    393  * Save sector.
    394  */
    395 int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
    396 {
    397     if (!(opt.sect && opt.save))
    398 	return 0;
    399 
    400     if (memcmp(org, data->data, data->size)) {
    401 	if (disk_write_sectors(&iter->di, iter->abs_lba, data->data, 1)) {
    402 	    error("Cannot write the updated sector.");
    403 	    goto bail;
    404 	}
    405 	/* function can be called again */
    406 	memcpy(org, data->data, data->size);
    407     }
    408 
    409     return 0;
    410 bail:
    411     return -1;
    412 }
    413 
    414 /*
    415  * To boot the Recovery Console of Windows NT/2K/XP we need to write
    416  * the string "cmdcons\0" to memory location 0000:7C03.
    417  * Memory location 0000:7C00 contains the bootsector of the partition.
    418  */
    419 int mangles_cmldr(struct data_area *data)
    420 {
    421     if (!(opt.sect && opt.cmldr))
    422 	return 0;
    423 
    424     memcpy((char *)data->data + 3, cmldr_signature, sizeof cmldr_signature);
    425     return 0;
    426 }
    427 
    428 /* Set common registers */
    429 int mangler_init(const struct part_iter *iter)
    430 {
    431     /* Set initial registry values */
    432     if (opt.file) {
    433 	opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.fseg;
    434 	opt.regs.ip = opt.fip;
    435     } else {
    436 	opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.sseg;
    437 	opt.regs.ip = opt.sip;
    438     }
    439 
    440     if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
    441 	opt.regs.esp.l = 0x7C00;
    442 
    443     /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
    444     opt.regs.ebx.b[0] = opt.regs.edx.b[0] = iter->di.disk;
    445 
    446     return 0;
    447 }
    448 
    449 /* ds:si & ds:bp */
    450 int mangler_handover(const struct part_iter *iter, const struct data_area *data)
    451 {
    452     if (opt.file && opt.maps && !opt.hptr) {
    453 	opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
    454 	opt.regs.ds = opt.sseg;
    455 	opt.regs.eax.l = 0;
    456     } else if (opt.hand) {
    457 	/* base is really 0x7be */
    458 	opt.regs.esi.l = opt.regs.ebp.l = data->base;
    459 	opt.regs.ds = 0;
    460 	if (iter->index && iter->type == typegpt)   /* must be iterated and GPT */
    461 	    opt.regs.eax.l = 0x54504721;	/* '!GPT' */
    462 	else
    463 	    opt.regs.eax.l = 0;
    464     }
    465 
    466     return 0;
    467 }
    468 
    469 /*
    470  * GRLDR of GRUB4DOS wants the partition number in DH:
    471  * -1:   whole drive (default)
    472  * 0-3:  primary partitions
    473  * 4-*:  logical partitions
    474  */
    475 int mangler_grldr(const struct part_iter *iter)
    476 {
    477     if (opt.grldr)
    478 	opt.regs.edx.b[1] = iter->index - 1;
    479 
    480     return 0;
    481 }
    482 
    483 /*
    484  * try to copy values from temporary iterator, if positions match
    485  */
    486 static void mbrcpy(struct part_iter *diter, struct part_iter *siter)
    487 {
    488     if (diter->dos.cebr_lba == siter->dos.cebr_lba &&
    489 	    diter->di.disk == siter->di.disk) {
    490 	memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr));
    491     }
    492 }
    493 
    494 static int fliphide(struct part_iter *iter, struct part_iter *miter)
    495 {
    496     struct disk_dos_part_entry *dp;
    497     static const uint16_t mask =
    498 	(1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
    499 	(1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
    500     uint8_t t;
    501 
    502     dp = (struct disk_dos_part_entry *)iter->record;
    503     t = dp->ostype;
    504 
    505     if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
    506 	/* It's a hideable partition type */
    507 	if (miter->index == iter->index || opt.hide & HIDE_REV)
    508 	    t &= ~0x10u;	/* unhide */
    509 	else
    510 	    t |= 0x10u;	/* hide */
    511     }
    512     if (dp->ostype != t) {
    513 	dp->ostype = t;
    514 	return -1;
    515     }
    516     return 0;
    517 }
    518 
    519 /*
    520  * miter - iterator we match against
    521  * hide bits meaning:
    522  * ..| - enable (1) / disable (0)
    523  * .|. - all (1) / pri (0)
    524  * |.. - unhide (1) / hide (0)
    525  */
    526 int manglepe_hide(struct part_iter *miter)
    527 {
    528     int wb = 0, werr = 0;
    529     struct part_iter *iter = NULL;
    530     int ridx;
    531 
    532     if (!(opt.hide & HIDE_ON))
    533 	return 0;
    534 
    535     if (miter->type != typedos) {
    536 	error("Option '[un]hide[all]' works only for legacy (DOS) partition scheme.");
    537 	return -1;
    538     }
    539 
    540     if (miter->index > 4 && !(opt.hide & HIDE_EXT))
    541 	warn("Specified partition is logical, so it can't be unhidden without 'unhideall'.");
    542 
    543     if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
    544 	return -1;
    545 
    546     while (!pi_next(iter) && !werr) {
    547 	ridx = iter->index0;
    548 	if (!(opt.hide & HIDE_EXT) && ridx > 3)
    549 	    break;  /* skip when we're constrained to pri only */
    550 
    551 	if (iter->index != -1)
    552 	    wb |= fliphide(iter, miter);
    553 
    554 	/*
    555 	 * we have to update mbr and each extended partition, but only if
    556 	 * changes (wb) were detected and there was no prior write error (werr)
    557 	 */
    558 	if (ridx >= 3 && wb && !werr) {
    559 	    mbrcpy(miter, iter);
    560 	    werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
    561 	    wb = 0;
    562 	}
    563     }
    564 
    565     if (iter->status < 0)
    566 	goto bail;
    567 
    568     /* last update */
    569     if (wb && !werr) {
    570 	mbrcpy(miter, iter);
    571 	werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
    572     }
    573     if (werr)
    574 	warn("Failed to write E/MBR during '[un]hide[all]'.");
    575 
    576 bail:
    577     pi_del(&iter);
    578     return 0;
    579 }
    580 
    581 static int updchs(struct part_iter *iter, int ext)
    582 {
    583     struct disk_dos_part_entry *dp;
    584     uint32_t ochs1, ochs2, lba;
    585 
    586     dp = (struct disk_dos_part_entry *)iter->record;
    587     if (!ext) {
    588 	/* primary or logical */
    589 	lba = (uint32_t)iter->abs_lba;
    590     } else {
    591 	/* extended */
    592 	dp += 1;
    593 	lba = iter->dos.nebr_lba;
    594     }
    595     ochs1 = *(uint32_t *)dp->start;
    596     ochs2 = *(uint32_t *)dp->end;
    597 
    598     /*
    599      * We have to be a bit more careful here in case of 0 start and/or length;
    600      * start = 0 would be converted to the beginning of the disk (C/H/S =
    601      * 0/0/1) or the [B]EBR, length = 0 would actually set the end CHS to be
    602      * lower than the start CHS.
    603      *
    604      * Both are harmless in case of a hole (and in non-hole case will make
    605      * partiter complain about corrupt layout if PIF_STRICT is set), but it
    606      * makes everything look silly and not really correct.
    607      *
    608      * Thus the approach as seen below.
    609      */
    610 
    611     if (dp->start_lba || iter->index != -1) {
    612 	lba2chs(&dp->start, &iter->di, lba, L2C_CADD);
    613     } else {
    614 	memset(&dp->start, 0, sizeof dp->start);
    615     }
    616 
    617     if ((dp->start_lba || iter->index != -1) && dp->length) {
    618 	lba2chs(&dp->end, &iter->di, lba + dp->length - 1, L2C_CADD);
    619     } else {
    620 	memset(&dp->end, 0, sizeof dp->end);
    621     }
    622 
    623     return
    624 	*(uint32_t *)dp->start != ochs1 ||
    625 	*(uint32_t *)dp->end != ochs2;
    626 }
    627 
    628 /*
    629  * miter - iterator we match against
    630  */
    631 int manglepe_fixchs(struct part_iter *miter)
    632 {
    633     int wb = 0, werr = 0;
    634     struct part_iter *iter = NULL;
    635     int ridx;
    636 
    637     if (!opt.fixchs)
    638 	return 0;
    639 
    640     if (miter->type != typedos) {
    641 	error("Option 'fixchs' works only for legacy (DOS) partition scheme.");
    642 	return -1;
    643     }
    644 
    645     if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
    646 	return -1;
    647 
    648     while (!pi_next(iter) && !werr) {
    649 	ridx = iter->index0;
    650 
    651 	wb |= updchs(iter, 0);
    652 	if (ridx > 3)
    653 	    wb |= updchs(iter, 1);
    654 
    655 	/*
    656 	 * we have to update mbr and each extended partition, but only if
    657 	 * changes (wb) were detected and there was no prior write error (werr)
    658 	 */
    659 	if (ridx >= 3 && wb && !werr) {
    660 	    mbrcpy(miter, iter);
    661 	    werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
    662 	    wb = 0;
    663 	}
    664     }
    665 
    666     if (iter->status < 0)
    667 	goto bail;
    668 
    669     /* last update */
    670     if (wb && !werr) {
    671 	mbrcpy(miter, iter);
    672 	werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
    673     }
    674     if (werr)
    675 	warn("Failed to write E/MBR during 'fixchs'.");
    676 
    677 bail:
    678     pi_del(&iter);
    679     return 0;
    680 }
    681 
    682 /* vim: set ts=8 sts=4 sw=4 noet: */
    683