• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
manglef_isolinux(struct data_area * data)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  */
manglef_grub(const struct part_iter * iter,struct data_area * data)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 */
mangle_bpb(const struct part_iter * iter,struct data_area * data,const char * tag)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  */
manglef_bpb(const struct part_iter * iter,struct data_area * data)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  */
mangles_bpb(const struct part_iter * iter,struct data_area * data)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  */
manglesf_bss(struct data_area * sec,struct data_area * fil)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  */
mangles_save(const struct part_iter * iter,const struct data_area * data,void * org)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  */
mangles_cmldr(struct data_area * data)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 */
mangler_init(const struct part_iter * iter)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 */
mangler_handover(const struct part_iter * iter,const struct data_area * data)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  */
mangler_grldr(const struct part_iter * iter)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  */
mbrcpy(struct part_iter * diter,struct part_iter * siter)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 
fliphide(struct part_iter * iter,struct part_iter * miter)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  */
manglepe_hide(struct part_iter * miter)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 
updchs(struct part_iter * iter,int ext)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  */
manglepe_fixchs(struct part_iter * miter)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