Expand PMF_FN_* macros.
[netbsd-mini2440.git] / distrib / utils / sysinst / mbr.c
blobc5b1c914ca6318040fb6b7f16ca6a0a7e6531cb0
1 /* $NetBSD: mbr.c,v 1.81 2009/09/19 14:57:27 abs Exp $ */
3 /*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Piermont Information Systems Inc.
21 * 4. The name of Piermont Information Systems Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
25 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35 * THE POSSIBILITY OF SUCH DAMAGE.
40 * Following applies to the geometry guessing code
44 * Mach Operating System
45 * Copyright (c) 1992 Carnegie Mellon University
46 * All Rights Reserved.
48 * Permission to use, copy, modify and distribute this software and its
49 * documentation is hereby granted, provided that both the copyright
50 * notice and this permission notice appear in all copies of the
51 * software, derivative works or modified versions, and any portions
52 * thereof, and that both notices appear in supporting documentation.
54 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
55 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
56 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
58 * Carnegie Mellon requests users of this software to return to
60 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
61 * School of Computer Science
62 * Carnegie Mellon University
63 * Pittsburgh PA 15213-3890
65 * any improvements or extensions that they make and grant Carnegie Mellon
66 * the rights to redistribute these changes.
69 /* mbr.c -- DOS Master Boot Record editing code */
71 #include <sys/param.h>
72 #include <sys/types.h>
73 #include <stdio.h>
74 #include <unistd.h>
75 #include <fcntl.h>
76 #include <util.h>
77 #include "defs.h"
78 #include "mbr.h"
79 #include "md.h"
80 #include "msg_defs.h"
81 #include "menu_defs.h"
82 #include "endian.h"
84 #define NO_BOOTMENU (-0x100)
86 #define MAXCYL 1023 /* Possibly 1024 */
87 #define MAXHEAD 255 /* Possibly 256 */
88 #define MAXSECTOR 63
90 struct part_id {
91 int id;
92 const char *name;
93 } part_ids[] = {
94 {0, "unused"},
95 {MBR_PTYPE_NETBSD, "NetBSD"},
96 {MBR_PTYPE_EXT_LBA, "Extended partition, LBA"},
97 {MBR_PTYPE_386BSD, "FreeBSD/386BSD"},
98 {MBR_PTYPE_OPENBSD, "OpenBSD"},
99 {MBR_PTYPE_LNXEXT2, "Linux native"},
100 {MBR_PTYPE_LNXSWAP, "Linux swap"},
101 {MBR_PTYPE_FAT12, "DOS FAT12"},
102 {MBR_PTYPE_FAT16S, "DOS FAT16, <32M"},
103 {MBR_PTYPE_FAT16B, "DOS FAT16, >32M"},
104 {MBR_PTYPE_FAT16L, "Windows FAT16, LBA"},
105 {MBR_PTYPE_FAT32, "Windows FAT32"},
106 {MBR_PTYPE_FAT32L, "Windows FAT32, LBA"},
107 {MBR_PTYPE_NTFSVOL, "NTFS volume set"},
108 {MBR_PTYPE_NTFS, "NTFS"},
109 {MBR_PTYPE_PREP, "PReP Boot"},
110 #ifdef MBR_PTYPE_SOLARIS
111 {MBR_PTYPE_SOLARIS, "Solaris"},
112 #endif
113 {-1, "Unknown"},
116 static int get_mapping(struct mbr_partition *, int, int *, int *, int *,
117 daddr_t *);
118 static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *,
119 uint8_t *, uint32_t);
122 * Notes on the extended partition editor.
124 * The extended partition structure is actually a singly linked list.
125 * Each of the 'mbr' sectors can only contain 2 items, the first describes
126 * a user partition (relative to that mbr sector), the second describes
127 * the following partition (relative to the start of the extended partition).
129 * The 'start' sector for the user partition is always the size of one
130 * track - very often 63. The extended partitions themselves should
131 * always start on a cylinder boundary using the BIOS geometry - often
132 * 16065 sectors per cylinder.
134 * The disk is also always described in increasing sector order.
136 * During editing we keep the mbr sectors accurate (it might have been
137 * easier to use absolute sector numbers though), and keep a copy of the
138 * entire sector - to preserve any information any other OS has tried
139 * to squirrel away in the (apparently) unused space.
141 * For simplicity we add entries for unused space. These should not
142 * get written to the disk.
144 * Typical disk (with some small numbers):
146 * 0 -> a 63 37 dos
147 * b 100 1000 extended LBA (type 15)
149 * 100 -> a 63 37 user
150 * b 100 200 extended partiton (type 5)
152 * 200 -> a 63 37 user
153 * b 200 300 extended partiton (type 5)
155 * 300 -> a 63 37 user
156 * b 0 0 0 (end of chain)
158 * If there is a gap, the 'b' partition will start beyond the area
159 * described by the 'a' partition.
161 * While writing this comment, I can't remember what happens is there
162 * is space at the start of the extended partition.
165 #ifndef debug_extended
166 #define dump_mbr(mbr, msg)
167 #else
168 void
169 dump_mbr(mbr_info_t *mbr, const char *msg)
171 int i;
173 fprintf(stderr, "%s: bsec %d\n", msg, bsec);
174 do {
175 fprintf(stderr, "%9p: %9d %9p %6.6s:",
176 mbr, mbr->sector, mbr->extended,
177 mbr->prev_ext, mbr->last_mounted);
178 for (i = 0; i < 4; i++)
179 fprintf(stderr, " %*d %9d %9d %9d,\n",
180 i ? 41 : 3,
181 mbr->mbr.mbr_parts[i].mbrp_type,
182 mbr->mbr.mbr_parts[i].mbrp_start,
183 mbr->mbr.mbr_parts[i].mbrp_size,
184 mbr->mbr.mbr_parts[i].mbrp_start +
185 mbr->mbr.mbr_parts[i].mbrp_size);
186 } while ((mbr = mbr->extended));
188 #endif
191 * To be used only on ports which cannot provide any bios geometry
194 set_bios_geom_with_mbr_guess(void)
196 int cyl, head;
197 daddr_t sec;
199 read_mbr(diskdev, &mbr);
200 msg_display(MSG_nobiosgeom, dlcyl, dlhead, dlsec);
201 if (guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec) >= 0)
202 msg_display_add(MSG_biosguess, cyl, head, sec);
203 set_bios_geom(cyl, head, sec);
204 return edit_mbr(&mbr);
208 * get C/H/S geometry from user via menu interface and
209 * store in globals.
211 void
212 set_bios_geom(int cyl, int head, int sec)
214 char res[80];
216 msg_display_add(MSG_setbiosgeom);
218 do {
219 snprintf(res, 80, "%d", sec);
220 msg_prompt_add(MSG_sectors, res, res, 80);
221 bsec = atoi(res);
222 } while (bsec <= 0 || bsec > 63);
224 do {
225 snprintf(res, 80, "%d", head);
226 msg_prompt_add(MSG_heads, res, res, 80);
227 bhead = atoi(res);
228 } while (bhead <= 0 || bhead > 256);
230 bcyl = dlsize / bsec / bhead;
231 if (dlsize != bcyl * bsec * bhead)
232 bcyl++;
235 #ifdef notdef
236 void
237 disp_cur_geom(void)
240 msg_display_add(MSG_realgeom, dlcyl, dlhead, dlsec);
241 msg_display_add(MSG_biosgeom, bcyl, bhead, bsec);
243 #endif
247 * Then, the partition stuff...
251 * If we change the mbr partitioning, the we must remove any references
252 * in the netbsd disklabel to the part we changed.
254 static void
255 remove_old_partitions(uint start, int64_t size)
257 partinfo *p;
258 uint end;
260 if (size > 0) {
261 end = start + size;
262 } else {
263 end = start;
264 start = end - size;
267 if (end == 0)
268 return;
270 for (p = oldlabel; p < oldlabel + nelem(oldlabel); p++) {
271 if (p->pi_offset >= end || p->pi_offset + p->pi_size <= start)
272 continue;
273 memset(p, 0, sizeof *p);
277 static int
278 find_mbr_space(struct mbr_sector *mbrs, uint *start, uint *size, uint from, int ignore)
280 uint sz;
281 int i;
282 uint s, e;
284 check_again:
285 sz = dlsize - from;
286 for (i = 0; i < MBR_PART_COUNT; i++) {
287 if (i == ignore)
288 continue;
289 s = mbrs->mbr_parts[i].mbrp_start;
290 e = s + mbrs->mbr_parts[i].mbrp_size;
291 if (s <= from && e > from) {
292 from = e;
293 goto check_again;
295 if (s > from && s - from < sz)
296 sz = s - from;
298 if (sz == 0)
299 return -1;
300 if (start != NULL)
301 *start = from;
302 if (size != NULL)
303 *size = sz;
304 return 0;
307 static struct mbr_partition *
308 get_mbrp(mbr_info_t **mbrip, int opt)
310 mbr_info_t *mbri = *mbrip;
312 if (opt >= MBR_PART_COUNT)
313 for (opt -= MBR_PART_COUNT - 1; opt; opt--)
314 mbri = mbri->extended;
316 *mbrip = mbri;
317 return &mbri->mbr.mbr_parts[opt];
320 static int
321 err_msg_win(const char *errmsg)
323 const char *cont;
324 int l, l1;
326 errmsg = msg_string(errmsg);
327 cont = msg_string(MSG_Hit_enter_to_continue);
329 l = strlen(errmsg);
330 l1 = strlen(cont);
331 if (l < l1)
332 l = l1;
334 msg_prompt_win("%s.\n%s", -1, 18, l + 5, 4,
335 NULL, NULL, 1, errmsg, cont);
336 return 0;
339 static int
340 set_mbr_type(menudesc *m, void *arg)
342 mbr_info_t *mbri = arg;
343 mbr_info_t *ombri = arg;
344 mbr_info_t *ext;
345 struct mbr_partition *mbrp;
346 char *cp;
347 int opt = mbri->opt;
348 int type;
349 u_int start, sz;
350 int i;
351 char numbuf[5];
353 dump_mbr(ombri, "set type");
355 mbrp = get_mbrp(&mbri, opt);
356 if (opt >= MBR_PART_COUNT)
357 opt = 0;
359 type = m->cursel;
360 if (type == 0)
361 return 1;
362 type = part_ids[type - 1].id;
363 while (type == -1) {
364 snprintf(numbuf, sizeof numbuf, "%u", mbrp->mbrp_type);
365 msg_prompt_win(MSG_get_ptn_id, -1, 18, 0, 0,
366 numbuf, numbuf, sizeof numbuf);
367 type = strtoul(numbuf, &cp, 0);
368 if (*cp != 0)
369 type = -1;
372 if (type == mbrp->mbrp_type)
373 /* type not changed... */
374 return 1;
376 mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
378 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
379 /* deleting extended partition.... */
380 if (mbri->sector || mbri->extended->extended)
381 /* We should have stopped this happening... */
382 return err_msg_win("can't delete extended");
383 free(mbri->extended);
384 mbri->extended = NULL;
387 if (type == 0) {
388 /* Deleting partition */
389 mbrp->mbrp_type = 0;
390 /* Remove references to this space from the NetBSD label */
391 remove_old_partitions(mbri->sector + mbrp->mbrp_start,
392 mbrp->mbrp_size);
393 #ifdef BOOTSEL
394 if (ombri->bootsec == mbri->sector + mbrp->mbrp_start)
395 ombri->bootsec = 0;
397 memset(mbri->mbrb.mbrbs_nametab[opt], 0,
398 sizeof mbri->mbrb.mbrbs_nametab[opt]);
399 #endif
400 if (mbri->sector == 0) {
401 /* A main partition */
402 memset(mbrp, 0, sizeof *mbrp);
403 return 1;
406 /* Merge with previous and next free areas */
407 ext = mbri->prev_ext;
408 if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
409 /* previous was free - back up one entry */
410 mbri = ext;
411 ombri->opt--;
413 while ((ext = mbri->extended)) {
414 if (ext->mbr.mbr_parts[0].mbrp_type != 0)
415 break;
416 sz = ext->mbr.mbr_parts[0].mbrp_start +
417 ext->mbr.mbr_parts[0].mbrp_size;
418 /* Increase size of our (empty) partition */
419 mbri->mbr.mbr_parts[0].mbrp_size += sz;
420 /* Make us describe the next partition */
421 mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
422 /* fix list of extended partitions */
423 mbri->extended = ext->extended;
424 if (ext->extended != NULL)
425 ext->extended->prev_ext = mbri;
426 free(ext);
427 /* Make previous size cover all our ptn */
428 ext = mbri->prev_ext;
429 if (ext != NULL)
430 ext->mbr.mbr_parts[1].mbrp_size += sz;
432 return 1;
435 if (mbrp->mbrp_start == 0) {
436 /* Find first chunk of space... */
437 /* Must be in the main partition */
438 if (mbri->sector != 0)
439 /* shouldn't be possible to have null start... */
440 return err_msg_win("main-extended mixup");
441 if (find_mbr_space(&mbri->mbr, &start, &sz, bsec, -1) != 0)
442 /* no space */
443 return err_msg_win(MSG_No_free_space);
444 mbrp->mbrp_start = start;
445 mbrp->mbrp_size = sz;
446 /* If there isn't an active partition mark this one active */
447 if (!MBR_IS_EXTENDED(type)) {
448 for (i = 0; i < MBR_PART_COUNT; i++)
449 if (mbri->mbr.mbr_parts[i].mbrp_flag != 0)
450 break;
451 if (i == MBR_PART_COUNT)
452 mbrp->mbrp_flag = MBR_PFLAG_ACTIVE;
456 if (MBR_IS_EXTENDED(type)) {
457 if (mbri->sector != 0)
458 /* Can't set extended partition in an extended one */
459 return err_msg_win(MSG_Only_one_extended_ptn);
460 if (mbri->extended)
461 /* Can't have two extended partitions */
462 return err_msg_win(MSG_Only_one_extended_ptn);
463 /* Create new extended partition */
464 ext = calloc(1, sizeof *mbri->extended);
465 if (!ext)
466 return 0;
467 mbri->extended = ext;
468 ext->sector = mbrp->mbrp_start;
469 ext->mbr.mbr_parts[0].mbrp_start = bsec;
470 ext->mbr.mbr_parts[0].mbrp_size = mbrp->mbrp_size - bsec;
472 mbrp->mbrp_type = type;
474 return 1;
477 static void
478 set_type_label(menudesc *m, int opt, void *arg)
481 if (opt == 0) {
482 wprintw(m->mw, msg_string(MSG_Dont_change));
483 return;
485 if (opt == 1) {
486 wprintw(m->mw, msg_string(MSG_Delete_partition));
487 return;
489 if (part_ids[opt - 1].id == -1) {
490 wprintw(m->mw, msg_string(MSG_Other_kind));
491 return;
493 wprintw(m->mw, part_ids[opt - 1].name);
496 static int
497 edit_mbr_type(menudesc *m, void *arg)
499 static menu_ent type_opts[1 + nelem(part_ids)];
500 static int type_menu = -1;
501 unsigned int i;
503 if (type_menu == -1) {
504 for (i = 0; i < nelem(type_opts); i++) {
505 type_opts[i].opt_menu = OPT_NOMENU;
506 type_opts[i].opt_action = set_mbr_type;
508 type_menu = new_menu(NULL, type_opts, nelem(type_opts),
509 13, 12, 0, 30,
510 MC_SUBMENU | MC_SCROLL | MC_NOEXITOPT | MC_NOCLEAR,
511 NULL, set_type_label, NULL,
512 NULL, NULL);
515 if (type_menu != -1)
516 process_menu(type_menu, arg);
518 return 0;
521 static int
522 edit_mbr_start(menudesc *m, void *arg)
524 mbr_info_t *mbri = arg;
525 mbr_info_t *ext;
526 struct mbr_partition *mbrp;
527 int opt = mbri->opt;
528 uint start, sz;
529 uint new_r, new, limit, dflt_r;
530 int64_t delta;
531 const char *errmsg;
532 char *cp;
533 struct {
534 uint start;
535 uint start_r;
536 uint limit;
537 } freespace[MBR_PART_COUNT];
538 unsigned int spaces;
539 unsigned int i;
540 char prompt[MBR_PART_COUNT * 60];
541 unsigned int len;
542 char numbuf[12];
544 if (opt >= MBR_PART_COUNT)
545 /* should not be able to get here... */
546 return 1;
548 mbrp = mbri->mbr.mbr_parts + opt;
549 /* locate the start of all free areas */
550 spaces = 0;
551 for (start = bsec, i = 0; i < MBR_PART_COUNT; start += sz, i++) {
552 if (find_mbr_space(&mbri->mbr, &start, &sz, start, opt))
553 break;
554 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
555 /* Only want the area that contains this partition */
556 if (mbrp->mbrp_start < start ||
557 mbrp->mbrp_start >= start + sz)
558 continue;
559 i = MBR_PART_COUNT - 1;
561 freespace[spaces].start = start;
562 freespace[spaces].start_r = start / sizemult;
563 freespace[spaces].limit = start + sz;
564 if (++spaces >= sizeof freespace)
565 /* shouldn't happen... */
566 break;
569 /* Add description of start/size to user prompt */
570 len = 0;
571 for (i = 0; i < spaces; i++) {
572 len += snprintf(prompt + len, sizeof prompt - len,
573 msg_string(MSG_ptn_starts),
574 freespace[i].start_r,
575 freespace[i].limit / sizemult, multname,
576 freespace[i].limit / sizemult - freespace[i].start_r,
577 multname);
578 if (len >= sizeof prompt)
579 break;
582 /* And loop until the user gives a sensible answer */
583 dflt_r = mbrp->mbrp_start / sizemult;
584 errmsg = "";
585 for (;;) {
586 snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
587 msg_prompt_win(MSG_get_ptn_start, -1, 18, 60, spaces + 3,
588 numbuf, numbuf, sizeof numbuf,
589 prompt, msg_string(errmsg), multname);
590 new_r = strtoul(numbuf, &cp, 0);
591 if (*cp != 0) {
592 errmsg = MSG_Invalid_numeric;
593 continue;
595 if (new_r == dflt_r)
596 /* Unchanged */
597 return 0;
599 * Check that the start address from the user is inside one
600 * of the free areas.
602 new = new_r * sizemult;
603 for (i = 0; i < spaces; i++) {
604 if (new_r == freespace[i].start_r) {
605 new = freespace[i].start;
606 break;
608 if (new >= freespace[i].start &&
609 new < freespace[i].limit)
610 break;
612 if (i >= spaces) {
613 errmsg = MSG_Space_allocated;
614 continue;
616 limit = freespace[i].limit;
618 * We can only increase the start of an extended partition
619 * if the corresponding space inside the partition isn't used.
621 if (new > mbrp->mbrp_start &&
622 MBR_IS_EXTENDED(mbrp->mbrp_type) &&
623 (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
624 mbri->extended->mbr.mbr_parts[0].mbrp_size <
625 new - mbrp->mbrp_start)) {
626 errmsg = MSG_Space_allocated;
627 continue;
629 break;
632 if (new < mbrp->mbrp_start + mbrp->mbrp_size &&
633 limit > mbrp->mbrp_start)
634 /* Keep end of partition in the same place */
635 limit = mbrp->mbrp_start + mbrp->mbrp_size;
637 delta = new - mbrp->mbrp_start;
638 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
639 ext = mbri->extended;
640 if (ext->mbr.mbr_parts[0].mbrp_type != 0) {
641 /* allocate an extended ptn for the free item */
642 ext = calloc(1, sizeof *ext);
643 if (!ext)
644 return 0;
645 ext->sector = mbrp->mbrp_start;
646 ext->extended = mbri->extended;
647 mbri->extended->prev_ext = ext;
648 mbri->extended = ext;
649 ext->mbr.mbr_parts[0].mbrp_start = bsec;
650 ext->mbr.mbr_parts[0].mbrp_size = -bsec;
651 ext->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
652 ext->mbr.mbr_parts[1].mbrp_start = 0;
653 ext->mbr.mbr_parts[1].mbrp_size =
654 ext->extended->mbr.mbr_parts[0].mbrp_start +
655 ext->extended->mbr.mbr_parts[0].mbrp_size;
657 /* adjust size of first free item */
658 ext->mbr.mbr_parts[0].mbrp_size -= delta;
659 ext->sector += delta;
660 /* and the link of all extended partitions */
662 if (ext->extended)
663 ext->mbr.mbr_parts[1].mbrp_start -= delta;
664 while ((ext = ext->extended));
666 remove_old_partitions(mbri->sector + mbrp->mbrp_start, delta);
668 /* finally set partition base and size */
669 mbrp->mbrp_start = new;
670 mbrp->mbrp_size = limit - new;
671 mbri->last_mounted[opt] = NULL;
673 return 0;
676 static int
677 edit_mbr_size(menudesc *m, void *arg)
679 mbr_info_t *mbri = arg;
680 mbr_info_t *ombri = arg;
681 mbr_info_t *ext;
682 struct mbr_partition *mbrp;
683 int opt = mbri->opt;
684 uint start, max, max_r, dflt, dflt_r, new;
685 uint freespace;
686 int delta;
687 char numbuf[12];
688 char *cp;
689 const char *errmsg;
691 mbrp = get_mbrp(&mbri, opt);
692 dflt = mbrp->mbrp_size;
693 if (opt < MBR_PART_COUNT) {
694 max = 0;
695 find_mbr_space(&mbri->mbr, &start, &max, mbrp->mbrp_start, opt);
696 if (start != mbrp->mbrp_start)
697 return 0;
698 if (dflt == 0)
699 dflt = max;
700 } else {
701 ext = mbri->extended;
702 max = dflt;
704 * If the next extended partition describes a free area,
705 * then merge it onto this area.
707 if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
708 if (ext->extended)
709 ext->extended->prev_ext = mbri;
710 mbri->extended = ext->extended;
711 if (mbri->prev_ext)
712 mbri->prev_ext->mbr.mbr_parts[1].mbrp_size
713 += mbri->mbr.mbr_parts[1].mbrp_size;
714 mbrp->mbrp_size += mbri->mbr.mbr_parts[1].mbrp_size;
715 max += mbri->mbr.mbr_parts[1].mbrp_size;
716 mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
717 free(ext);
721 start = mbri->sector + mbrp->mbrp_start;
722 /* We need to keep both the unrounded and rounded (_r) max and dflt */
723 dflt_r = (start + dflt) / sizemult - start / sizemult;
724 if (max == dflt)
725 max_r = dflt_r;
726 else
727 max_r = max / sizemult;
728 for (errmsg = "";;) {
729 snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
730 msg_prompt_win(MSG_get_ptn_size, -1, 18, 0, 0,
731 numbuf, numbuf, sizeof numbuf,
732 msg_string(errmsg), max_r, multname);
733 new = strtoul(numbuf, &cp, 0);
734 if (*cp != 0) {
735 errmsg = MSG_Invalid_numeric;
736 continue;
738 if (new > max_r) {
739 errmsg = MSG_Too_large;
740 continue;
742 if (new == 0)
743 /* Treat zero as a request for the maximum */
744 new = max_r;
745 if (new == dflt_r)
746 /* If unchanged, don't re-round size */
747 new = dflt;
748 else {
749 /* Round end to cylinder boundary */
750 if (sizemult != 1) {
751 new *= sizemult;
752 new += rounddown(start, current_cylsize);
753 new = roundup(new, current_cylsize);
754 new -= start;
755 while (new <= 0)
756 new += current_cylsize;
759 if (new > max)
760 /* We rounded the value to above the max */
761 new = max;
763 if (new == dflt || opt >= MBR_PART_COUNT
764 || !MBR_IS_EXTENDED(mbrp->mbrp_type))
765 break;
767 * We've been asked to change the size of the main extended
768 * partition. If this reduces the size, then that space
769 * must be unallocated. If it increases the size then
770 * we must add a description ofthe new free space.
772 /* Find last extended partition */
773 for (ext = mbri->extended; ext->extended; ext = ext->extended)
774 continue;
775 if ((new < dflt && (ext->mbr.mbr_parts[0].mbrp_type != 0
776 || (mbrp->mbrp_start + new < ext->sector + bsec
777 && mbrp->mbrp_start + new != ext->sector)))
778 || (new > dflt && ext->mbr.mbr_parts[0].mbrp_type != 0
779 && new < dflt + bsec)) {
780 errmsg = MSG_Space_allocated;
781 continue;
783 delta = new - dflt;
784 if (ext->mbr.mbr_parts[0].mbrp_type == 0) {
785 /* adjust size of last item (free space) */
786 if (mbrp->mbrp_start + new == ext->sector) {
787 /* kill last extended ptn */
788 ext = ext->prev_ext;
789 free(ext->extended);
790 ext->extended = NULL;
791 memset(&ext->mbr.mbr_parts[1], 0,
792 sizeof ext->mbr.mbr_parts[1]);
793 break;
795 ext->mbr.mbr_parts[0].mbrp_size += delta;
796 ext = ext->prev_ext;
797 if (ext != NULL)
798 ext->mbr.mbr_parts[1].mbrp_size += delta;
799 break;
801 /* Joy of joys, we must allocate another extended ptn */
802 mbri = ext;
803 ext = calloc(1, sizeof *ext);
804 if (!ext)
805 return 0;
806 mbri->extended = ext;
807 ext->prev_ext = mbri;
808 ext->mbr.mbr_parts[0].mbrp_start = bsec;
809 ext->mbr.mbr_parts[0].mbrp_size = delta - bsec;
810 ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
811 + mbri->mbr.mbr_parts[0].mbrp_size;
812 mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
813 mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
814 mbri->mbr.mbr_parts[1].mbrp_size = delta;
815 break;
818 if (opt >= MBR_PART_COUNT && max - new <= (uint32_t)bsec)
819 /* Round up if not enough space for a header for free area */
820 new = max;
822 if (new != mbrp->mbrp_size) {
823 /* Kill information about old partition from label */
824 mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
825 remove_old_partitions(mbri->sector + mbrp->mbrp_start +
826 mbrp->mbrp_size, (int64_t)new - mbrp->mbrp_size);
829 mbrp->mbrp_size = new;
830 if (opt < MBR_PART_COUNT || new == max)
831 return 0;
833 /* Add extended partition for the free space */
834 ext = calloc(1, sizeof *ext);
835 if (!ext) {
836 mbrp->mbrp_size = max;
837 return 0;
839 /* Link into our extended chain */
840 ext->extended = mbri->extended;
841 mbri->extended = ext;
842 ext->prev_ext = mbri;
843 if (ext->extended != NULL)
844 ext->extended->prev_ext = ext;
845 ext->mbr.mbr_parts[1] = mbri->mbr.mbr_parts[1];
846 freespace = max - new;
847 if (mbri->prev_ext != NULL)
848 mbri->prev_ext->mbr.mbr_parts[1].mbrp_size -= freespace;
850 ext->mbr.mbr_parts[0].mbrp_start = bsec;
851 ext->mbr.mbr_parts[0].mbrp_size = freespace - bsec;
853 ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
854 + mbri->mbr.mbr_parts[0].mbrp_size;
855 mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
856 mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
857 mbri->mbr.mbr_parts[1].mbrp_size = freespace;
859 return 0;
862 static int
863 edit_mbr_active(menudesc *m, void *arg)
865 mbr_info_t *mbri = arg;
866 int i;
867 uint8_t *fl;
869 if (mbri->opt >= MBR_PART_COUNT)
870 /* sanity */
871 return 0;
873 /* Invert active flag */
874 fl = &mbri->mbr.mbr_parts[mbri->opt].mbrp_flag;
875 if (*fl == MBR_PFLAG_ACTIVE) {
876 *fl = 0;
877 return 0;
880 /* Ensure there is at most one active partition */
881 for (i = 0; i < MBR_PART_COUNT; i++)
882 mbri->mbr.mbr_parts[i].mbrp_flag = 0;
883 *fl = MBR_PFLAG_ACTIVE;
885 return 0;
888 static int
889 edit_mbr_install(menudesc *m, void *arg)
891 mbr_info_t *mbri = arg;
892 mbr_info_t *ombri = arg;
893 struct mbr_partition *mbrp;
894 int opt = mbri->opt;
895 uint start;
897 mbrp = get_mbrp(&mbri, opt);
898 if (opt >= MBR_PART_COUNT)
899 opt = 0;
901 start = mbri->sector + mbrp->mbrp_start;
902 /* We just remember the start address of the partition... */
903 if (start == ombri->install)
904 ombri->install = 0;
905 else
906 ombri->install = start;
907 return 0;
910 #ifdef BOOTSEL
911 static int
912 edit_mbr_bootmenu(menudesc *m, void *arg)
914 mbr_info_t *mbri = arg;
915 mbr_info_t *ombri = arg;
916 struct mbr_partition *mbrp;
917 int opt = mbri->opt;
919 mbrp = get_mbrp(&mbri, opt);
920 if (opt >= MBR_PART_COUNT)
921 opt = 0;
923 msg_prompt_win(/* XXX translate? */ "bootmenu", -1, 18, 0, 0,
924 mbri->mbrb.mbrbs_nametab[opt],
925 mbri->mbrb.mbrbs_nametab[opt],
926 sizeof mbri->mbrb.mbrbs_nametab[opt]);
927 if (mbri->mbrb.mbrbs_nametab[opt][0] == ' ')
928 mbri->mbrb.mbrbs_nametab[opt][0] = 0;
929 if (mbri->mbrb.mbrbs_nametab[opt][0] == 0
930 && ombri->bootsec == mbri->sector + mbrp->mbrp_start)
931 ombri->bootsec = 0;
932 return 0;
935 static int
936 edit_mbr_bootdefault(menudesc *m, void *arg)
938 mbr_info_t *mbri = arg;
939 mbr_info_t *ombri = arg;
940 struct mbr_partition *mbrp;
942 mbrp = get_mbrp(&mbri, mbri->opt);
944 ombri->bootsec = mbri->sector + mbrp->mbrp_start;
945 return 0;
947 #endif
949 static void set_ptn_label(menudesc *m, int line, void *arg);
950 static void set_ptn_header(menudesc *m, void *arg);
952 static int
953 edit_mbr_entry(menudesc *m, void *arg)
955 mbr_info_t *mbri = arg;
956 static int ptn_menu = -1;
958 static menu_ent ptn_opts[] = {
959 #define PTN_OPT_TYPE 0
960 {NULL, OPT_NOMENU, 0, edit_mbr_type},
961 #define PTN_OPT_START 1
962 {NULL, OPT_NOMENU, 0, edit_mbr_start},
963 #define PTN_OPT_SIZE 2
964 {NULL, OPT_NOMENU, 0, edit_mbr_size},
965 #define PTN_OPT_END 3
966 {NULL, OPT_NOMENU, OPT_IGNORE, NULL}, /* display end */
967 #define PTN_OPT_ACTIVE 4
968 {NULL, OPT_NOMENU, 0, edit_mbr_active},
969 #define PTN_OPT_INSTALL 5
970 {NULL, OPT_NOMENU, 0, edit_mbr_install},
971 #ifdef BOOTSEL
972 #define PTN_OPT_BOOTMENU 6
973 {NULL, OPT_NOMENU, 0, edit_mbr_bootmenu},
974 #define PTN_OPT_BOOTDEFAULT 7
975 {NULL, OPT_NOMENU, 0, edit_mbr_bootdefault},
976 #endif
977 {MSG_askunits, MENU_sizechoice, OPT_SUB, NULL},
980 if (ptn_menu == -1)
981 ptn_menu = new_menu(NULL, ptn_opts, nelem(ptn_opts),
982 15, 6, 0, 54,
983 MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
984 set_ptn_header, set_ptn_label, NULL,
985 NULL, MSG_Partition_OK);
986 if (ptn_menu == -1)
987 return 1;
989 mbri->opt = m->cursel;
990 process_menu(ptn_menu, mbri);
991 return 0;
994 static void
995 set_ptn_label(menudesc *m, int line, void *arg)
997 mbr_info_t *mbri = arg;
998 mbr_info_t *ombri = arg;
999 struct mbr_partition *mbrp;
1000 int opt;
1001 static const char *yes, *no;
1003 if (yes == NULL) {
1004 yes = msg_string(MSG_Yes);
1005 no = msg_string(MSG_No);
1008 opt = mbri->opt;
1009 mbrp = get_mbrp(&mbri, opt);
1010 if (opt >= MBR_PART_COUNT)
1011 opt = 0;
1013 switch (line) {
1014 case PTN_OPT_TYPE:
1015 wprintw(m->mw, msg_string(MSG_ptn_type),
1016 get_partname(mbrp->mbrp_type));
1017 break;
1018 case PTN_OPT_START:
1019 wprintw(m->mw, msg_string(MSG_ptn_start),
1020 (mbri->sector + mbrp->mbrp_start) / sizemult, multname);
1021 break;
1022 case PTN_OPT_SIZE:
1023 wprintw(m->mw, msg_string(MSG_ptn_size),
1024 (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
1025 sizemult -
1026 (mbri->sector + mbrp->mbrp_start) / sizemult, multname);
1027 break;
1028 case PTN_OPT_END:
1029 wprintw(m->mw, msg_string(MSG_ptn_end),
1030 (mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
1031 sizemult, multname);
1032 break;
1033 case PTN_OPT_ACTIVE:
1034 wprintw(m->mw, msg_string(MSG_ptn_active),
1035 mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? yes : no);
1036 break;
1037 case PTN_OPT_INSTALL:
1038 wprintw(m->mw, msg_string(MSG_ptn_install),
1039 mbri->sector + mbrp->mbrp_start == ombri->install &&
1040 mbrp->mbrp_type == MBR_PTYPE_NETBSD ? yes : no);
1041 break;
1042 #ifdef BOOTSEL
1043 case PTN_OPT_BOOTMENU:
1044 wprintw(m->mw, msg_string(MSG_bootmenu),
1045 mbri->mbrb.mbrbs_nametab[opt]);
1046 break;
1047 case PTN_OPT_BOOTDEFAULT:
1048 wprintw(m->mw, msg_string(MSG_boot_dflt),
1049 ombri->bootsec == mbri->sector + mbrp->mbrp_start ? yes
1050 : no);
1051 break;
1052 #endif
1057 static void
1058 set_ptn_header(menudesc *m, void *arg)
1060 mbr_info_t *mbri = arg;
1061 struct mbr_partition *mbrp;
1062 int opt = mbri->opt;
1063 int typ;
1065 mbrp = get_mbrp(&mbri, opt);
1066 if (opt >= MBR_PART_COUNT)
1067 opt = 0;
1068 typ = mbrp->mbrp_type;
1070 #define DISABLE(opt,cond) \
1071 if (cond) \
1072 m->opts[opt].opt_flags |= OPT_IGNORE; \
1073 else \
1074 m->opts[opt].opt_flags &= ~OPT_IGNORE;
1076 /* Can't change type of the extended partition unless it is empty */
1077 DISABLE(PTN_OPT_TYPE, MBR_IS_EXTENDED(typ) &&
1078 (mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
1079 mbri->extended->extended != NULL));
1081 /* It is unnecessary to be able to change the base of an extended ptn */
1082 DISABLE(PTN_OPT_START, mbri->sector || typ == 0);
1084 /* or the size of a free area */
1085 DISABLE(PTN_OPT_SIZE, typ == 0);
1087 /* Only 'normal' partitions can be 'Active' */
1088 DISABLE(PTN_OPT_ACTIVE, mbri->sector != 0 || MBR_IS_EXTENDED(typ) || typ == 0);
1090 /* Can only install into NetBSD partition */
1091 DISABLE(PTN_OPT_INSTALL, typ != MBR_PTYPE_NETBSD);
1093 #ifdef BOOTSEL
1094 /* The extended partition isn't bootable */
1095 DISABLE(PTN_OPT_BOOTMENU, MBR_IS_EXTENDED(typ) || typ == 0);
1097 if (typ == 0)
1098 mbri->mbrb.mbrbs_nametab[opt][0] = 0;
1100 /* Only partitions with bootmenu names can be made the default */
1101 DISABLE(PTN_OPT_BOOTDEFAULT, mbri->mbrb.mbrbs_nametab[opt][0] == 0);
1102 #endif
1103 #undef DISABLE
1106 static void
1107 set_mbr_label(menudesc *m, int opt, void *arg)
1109 mbr_info_t *mbri = arg;
1110 mbr_info_t *ombri = arg;
1111 struct mbr_partition *mbrp;
1112 uint rstart, rend;
1113 const char *name, *cp, *mounted;
1114 int len;
1116 mbrp = get_mbrp(&mbri, opt);
1117 if (opt >= MBR_PART_COUNT)
1118 opt = 0;
1120 if (mbrp->mbrp_type == 0 && mbri->sector == 0) {
1121 len = snprintf(0, 0, msg_string(MSG_part_row_used), 0, 0, 0);
1122 wprintw(m->mw, "%*s", len, "");
1123 } else {
1124 rstart = mbri->sector + mbrp->mbrp_start;
1125 rend = (rstart + mbrp->mbrp_size) / sizemult;
1126 rstart = rstart / sizemult;
1127 wprintw(m->mw, msg_string(MSG_part_row_used),
1128 rstart, rend - rstart,
1129 mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? 'a' : ' ',
1130 #ifdef BOOTSEL
1131 ombri->bootsec == mbri->sector + mbrp->mbrp_start ? 'd' :
1132 #endif
1133 ' ',
1134 mbri->sector + mbrp->mbrp_start == ombri->install &&
1135 mbrp->mbrp_type == MBR_PTYPE_NETBSD ? 'I' : ' ');
1137 name = get_partname(mbrp->mbrp_type);
1138 mounted = mbri->last_mounted[opt];
1139 len = strlen(name);
1140 cp = strchr(name, ',');
1141 if (cp != NULL)
1142 len = cp - name;
1143 if (mounted && *mounted != 0) {
1144 wprintw(m->mw, " %*s (%s)", len, name, mounted);
1145 } else
1146 wprintw(m->mw, " %.*s", len, name);
1147 #ifdef BOOTSEL
1148 if (mbri->mbrb.mbrbs_nametab[opt][0] != 0) {
1149 int x, y;
1150 if (opt >= MBR_PART_COUNT)
1151 opt = 0;
1152 getyx(m->mw, y, x);
1153 if (x > 52) {
1154 x = 52;
1155 wmove(m->mw, y, x);
1157 wprintw(m->mw, "%*s %s", 53 - x, "",
1158 mbri->mbrb.mbrbs_nametab[opt]);
1160 #endif
1163 static void
1164 set_mbr_header(menudesc *m, void *arg)
1166 mbr_info_t *mbri = arg;
1167 static menu_ent *opts;
1168 static int num_opts;
1169 mbr_info_t *ext;
1170 menu_ent *op;
1171 int i;
1172 int left;
1174 msg_display(MSG_editparttable);
1176 msg_table_add(MSG_part_header, dlsize/sizemult, multname, multname,
1177 multname, multname);
1179 if (num_opts == 0) {
1180 num_opts = 6;
1181 opts = malloc(6 * sizeof *opts);
1182 if (opts == NULL) {
1183 m->numopts = 0;
1184 return;
1188 /* First four items are the main partitions */
1189 for (op = opts, i = 0; i < MBR_PART_COUNT; op++, i++) {
1190 op->opt_name = NULL;
1191 op->opt_menu = OPT_NOMENU;
1192 op->opt_flags = OPT_SUB;
1193 op->opt_action = edit_mbr_entry;
1195 left = num_opts - MBR_PART_COUNT;
1197 /* Followed by the extended partitions */
1198 for (ext = mbri->extended; ext; left--, op++, ext = ext->extended) {
1199 if (left <= 1) {
1200 menu_ent *new = realloc(opts,
1201 (num_opts + 4) * sizeof *opts);
1202 if (new == NULL)
1203 break;
1204 num_opts += 4;
1205 left += 4;
1206 op = new + (op - opts);
1207 opts = new;
1209 op->opt_name = NULL;
1210 op->opt_menu = OPT_NOMENU;
1211 op->opt_flags = 0;
1212 op->opt_action = edit_mbr_entry;
1215 /* and unit changer */
1216 op->opt_name = MSG_askunits;
1217 op->opt_menu = MENU_sizechoice;
1218 op->opt_flags = OPT_SUB;
1219 op->opt_action = NULL;
1220 op++;
1222 m->opts = opts;
1223 m->numopts = op - opts;
1227 mbr_use_wholedisk(mbr_info_t *mbri)
1229 struct mbr_sector *mbrs = &mbri->mbr;
1230 mbr_info_t *ext;
1231 struct mbr_partition *part;
1233 part = &mbrs->mbr_parts[0];
1234 /* Set the partition information for full disk usage. */
1235 while ((ext = mbri->extended)) {
1236 mbri->extended = ext->extended;
1237 free(ext);
1239 memset(part, 0, MBR_PART_COUNT * sizeof *part);
1240 #ifdef BOOTSEL
1241 memset(&mbri->mbrb, 0, sizeof mbri->mbrb);
1242 #endif
1243 part[0].mbrp_type = MBR_PTYPE_NETBSD;
1244 part[0].mbrp_size = dlsize - bsec;
1245 part[0].mbrp_start = bsec;
1246 part[0].mbrp_flag = MBR_PFLAG_ACTIVE;
1248 ptstart = bsec;
1249 ptsize = dlsize - bsec;
1250 return 1;
1254 * Let user change incore Master Boot Record partitions via menu.
1257 edit_mbr(mbr_info_t *mbri)
1259 struct mbr_sector *mbrs = &mbri->mbr;
1260 mbr_info_t *ext;
1261 struct mbr_partition *part;
1262 int i, j;
1263 int usefull;
1264 int mbr_menu;
1265 int activepart;
1266 int numbsd;
1267 uint bsdstart, bsdsize;
1268 uint start;
1270 /* Ask full/part */
1272 part = &mbrs->mbr_parts[0];
1273 msg_display(MSG_fullpart, diskdev);
1274 process_menu(MENU_fullpart, &usefull);
1276 /* DOS fdisk label checking and value setting. */
1277 if (usefull) {
1278 /* Count nonempty, non-BSD partitions. */
1279 numbsd = 0;
1280 for (i = 0; i < MBR_PART_COUNT; i++) {
1281 j = part[i].mbrp_type;
1282 if (j == 0)
1283 continue;
1284 numbsd++;
1285 if (j != MBR_PTYPE_NETBSD)
1286 numbsd++;
1289 /* Ask if we really want to blow away non-NetBSD stuff */
1290 if (numbsd > 1) {
1291 msg_display(MSG_ovrwrite);
1292 process_menu(MENU_noyes, NULL);
1293 if (!yesno) {
1294 if (logging)
1295 (void)fprintf(logfp, "User answered no to destroy other data, aborting.\n");
1296 return 0;
1299 return(md_mbr_use_wholedisk(mbri));
1302 mbr_menu = new_menu(NULL, NULL, 16, 0, -1, 15, 70,
1303 MC_NOBOX | MC_ALWAYS_SCROLL | MC_NOCLEAR,
1304 set_mbr_header, set_mbr_label, NULL,
1305 NULL, MSG_Partition_table_ok);
1306 if (mbr_menu == -1)
1307 return 0;
1309 /* Default to MB, and use bios geometry for cylinder size */
1310 set_sizemultname_meg();
1311 current_cylsize = bhead * bsec;
1313 for (;;) {
1314 ptstart = 0;
1315 ptsize = 0;
1316 process_menu(mbr_menu, mbri);
1318 activepart = 0;
1319 bsdstart = 0;
1320 bsdsize = 0;
1321 for (ext = mbri; ext; ext = ext->extended) {
1322 part = ext->mbr.mbr_parts;
1323 for (i = 0; i < MBR_PART_COUNT; part++, i++) {
1324 if (part->mbrp_flag != 0)
1325 activepart = 1;
1326 if (part->mbrp_type != MBR_PTYPE_NETBSD)
1327 continue;
1328 start = ext->sector + part->mbrp_start;
1329 if (start == mbri->install) {
1330 ptstart = mbri->install;
1331 ptsize = part->mbrp_size;
1333 if (bsdstart != 0)
1334 bsdstart = ~0;
1335 else {
1336 bsdstart = start;
1337 bsdsize = part->mbrp_size;
1342 /* Install in only netbsd partition if none tagged */
1343 if (ptstart == 0 && bsdstart != ~0u) {
1344 ptstart = bsdstart;
1345 ptsize = bsdsize;
1348 if (ptstart == 0) {
1349 if (bsdstart == 0)
1350 msg_display(MSG_nobsdpart);
1351 else
1352 msg_display(MSG_multbsdpart, 0);
1353 msg_display_add(MSG_reeditpart, 0);
1354 process_menu(MENU_yesno, NULL);
1355 if (!yesno)
1356 return 0;
1357 continue;
1360 if (activepart == 0) {
1361 msg_display(MSG_noactivepart);
1362 process_menu(MENU_yesno, NULL);
1363 if (yesno)
1364 continue;
1366 /* the md_check_mbr function has 3 ret codes to deal with
1367 * the different possible states. 0, 1, >1
1369 j = md_check_mbr(mbri);
1370 if (j == 0)
1371 return 0;
1372 if (j == 1)
1373 continue;
1375 break;
1378 free_menu(mbr_menu);
1380 return 1;
1383 const char *
1384 get_partname(int typ)
1386 int j;
1387 static char unknown[32];
1389 for (j = 0; part_ids[j].id != -1; j++)
1390 if (part_ids[j].id == typ)
1391 return part_ids[j].name;
1393 snprintf(unknown, sizeof unknown, "Unknown (%d)", typ);
1394 return unknown;
1398 read_mbr(const char *disk, mbr_info_t *mbri)
1400 struct mbr_partition *mbrp;
1401 struct mbr_sector *mbrs = &mbri->mbr;
1402 mbr_info_t *ext = NULL;
1403 char diskpath[MAXPATHLEN];
1404 int fd, i;
1405 uint32_t ext_base = 0, next_ext = 0, ext_size = 0;
1406 int rval = -1;
1407 #ifdef BOOTSEL
1408 mbr_info_t *ombri = mbri;
1409 int bootkey = 0;
1410 #endif
1413 * Fake up a likely 'bios sectors per track' for any extended
1414 * partition headers we might have to produce.
1416 if (bsec == 0)
1417 bsec = dlsec;
1419 memset(mbri, 0, sizeof *mbri);
1421 /* Open the disk. */
1422 fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
1423 if (fd < 0)
1424 goto bad_mbr;
1426 for (;;) {
1427 if (pread(fd, mbrs, sizeof *mbrs,
1428 (ext_base + next_ext) * (off_t)MBR_SECSIZE) - sizeof *mbrs != 0)
1429 break;
1431 if (!valid_mbr(mbrs))
1432 break;
1434 mbrp = &mbrs->mbr_parts[0];
1435 if (ext_base != 0) {
1436 /* sanity check extended chain */
1437 if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
1438 break;
1439 if (mbrp[1].mbrp_type != 0 &&
1440 !MBR_IS_EXTENDED(mbrp[1].mbrp_type))
1441 break;
1442 if (mbrp[2].mbrp_type != 0 || mbrp[3].mbrp_type != 0)
1443 break;
1444 /* Looks ok, link into extended chain */
1445 mbri->extended = ext;
1446 ext->prev_ext = next_ext != 0 ? mbri : NULL;
1447 ext->extended = NULL;
1448 mbri = ext;
1449 ext = NULL;
1451 #if BOOTSEL
1452 if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1453 /* old bootsel, grab bootsel info */
1454 mbri->mbrb = *(struct mbr_bootsel *)
1455 ((uint8_t *)mbrs + MBR_BS_OLD_OFFSET);
1456 if (ext_base == 0)
1457 bootkey = mbri->mbrb.mbrbs_defkey - SCAN_1;
1458 } else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
1459 /* new location */
1460 mbri->mbrb = mbrs->mbr_bootsel;
1461 if (ext_base == 0)
1462 bootkey = mbri->mbrb.mbrbs_defkey - SCAN_1;
1464 /* Save original flags for mbr code update tests */
1465 mbri->oflags = mbri->mbrb.mbrbs_flags;
1466 #endif
1467 mbri->sector = next_ext + ext_base;
1468 next_ext = 0;
1469 rval = 0;
1470 for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
1471 if (mbrp->mbrp_type == 0) {
1472 /* type is unused, discard scum */
1473 memset(mbrp, 0, sizeof *mbrp);
1474 continue;
1476 mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
1477 mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
1478 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
1479 next_ext = mbrp->mbrp_start;
1480 if (ext_base == 0)
1481 ext_size = mbrp->mbrp_size;
1482 } else {
1483 mbri->last_mounted[i] = strdup(get_last_mounted(
1484 fd, mbri->sector + mbrp->mbrp_start, NULL));
1485 #if BOOTSEL
1486 if (ombri->install == 0 &&
1487 strcmp(mbri->last_mounted[i], "/") == 0)
1488 ombri->install = mbri->sector +
1489 mbrp->mbrp_start;
1490 #endif
1492 #if BOOTSEL
1493 if (mbri->mbrb.mbrbs_nametab[i][0] != 0
1494 && bootkey-- == 0)
1495 ombri->bootsec = mbri->sector +
1496 mbrp->mbrp_start;
1497 #endif
1500 if (ext_base != 0) {
1501 /* Is there a gap before the next partition? */
1502 unsigned int limit = next_ext;
1503 unsigned int base;
1504 if (limit == 0)
1505 limit = ext_size;
1506 mbrp -= MBR_PART_COUNT;
1507 base =mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size;
1508 if (mbrp->mbrp_type != 0 && ext_base + limit != base) {
1509 /* Mock up an extry for the space */
1510 ext = calloc(1, sizeof *ext);
1511 if (!ext)
1512 break;
1513 ext->sector = base;
1514 ext->mbr.mbr_magic = htole16(MBR_MAGIC);
1515 ext->mbr.mbr_parts[1] = mbrp[1];
1516 ext->mbr.mbr_parts[0].mbrp_start = bsec;
1517 ext->mbr.mbr_parts[0].mbrp_size =
1518 ext_base + limit - base - bsec;
1519 mbrp[1].mbrp_type = MBR_PTYPE_EXT;
1520 mbrp[1].mbrp_start = base - ext_base;
1521 mbrp[1].mbrp_size = limit - mbrp[1].mbrp_start;
1522 mbri->extended = ext;
1523 ext->prev_ext = mbri;
1524 ext->extended = NULL;
1525 mbri = ext;
1526 ext = NULL;
1530 if (next_ext == 0 || ext_base + next_ext <= mbri->sector)
1531 break;
1532 if (ext_base == 0) {
1533 ext_base = next_ext;
1534 next_ext = 0;
1536 ext = calloc(sizeof *ext, 1);
1537 if (!ext)
1538 break;
1539 mbrs = &ext->mbr;
1542 bad_mbr:
1543 free(ext);
1544 if (fd >= 0)
1545 close(fd);
1546 if (rval == -1) {
1547 memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts);
1548 mbrs->mbr_magic = htole16(MBR_MAGIC);
1550 dump_mbr(ombri, "read");
1551 return rval;
1555 write_mbr(const char *disk, mbr_info_t *mbri, int convert)
1557 char diskpath[MAXPATHLEN];
1558 int fd, i, ret = 0;
1559 struct mbr_partition *mbrp;
1560 u_int32_t pstart, psize;
1561 #ifdef BOOTSEL
1562 struct mbr_sector *mbrs;
1563 #endif
1564 struct mbr_sector mbrsec;
1565 mbr_info_t *ext;
1566 uint sector;
1568 /* Open the disk. */
1569 fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0);
1570 if (fd < 0)
1571 return -1;
1573 #ifdef BOOTSEL
1575 * If the main boot code (appears to) contain the netbsd bootcode,
1576 * copy in all the menu strings and set the default keycode
1577 * to be that for the default partition.
1578 * Unfortunately we can't rely on the user having actually updated
1579 * to the new mbr code :-(
1581 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC)
1582 || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) {
1583 int8_t key = SCAN_1;
1584 uint offset = MBR_BS_OFFSET;
1585 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC))
1586 offset = MBR_BS_OLD_OFFSET;
1587 mbri->mbrb.mbrbs_defkey = SCAN_ENTER;
1588 if (mbri->mbrb.mbrbs_timeo == 0)
1589 mbri->mbrb.mbrbs_timeo = 182; /* 10 seconds */
1590 for (ext = mbri; ext != NULL; ext = ext->extended) {
1591 mbrs = &ext->mbr;
1592 mbrp = &mbrs->mbr_parts[0];
1593 /* Ensure marker is set in each sector */
1594 mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic;
1595 /* and copy in bootsel parameters */
1596 *(struct mbr_bootsel *)((uint8_t *)mbrs + offset) =
1597 ext->mbrb;
1598 for (i = 0; i < MBR_PART_COUNT; i++) {
1599 if (ext->mbrb.mbrbs_nametab[i][0] == 0)
1600 continue;
1601 if (ext->sector + mbrp->mbrp_start ==
1602 mbri->bootsec)
1603 mbri->mbrb.mbrbs_defkey = key;
1604 key++;
1607 /* copy main data (again) since we've put the 'key' in */
1608 *(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) =
1609 mbri->mbrb;
1611 #endif
1613 for (ext = mbri; ext != NULL; ext = ext->extended) {
1614 sector = ext->sector;
1615 mbrsec = ext->mbr; /* copy sector */
1616 mbrp = &mbrsec.mbr_parts[0];
1618 if (sector != 0 && ext->extended != NULL
1619 && ext->extended->mbr.mbr_parts[0].mbrp_type == 0) {
1620 /* We are followed by an empty slot, collapse out */
1621 ext = ext->extended;
1622 /* Make us describe the next non-empty partition */
1623 mbrp[1] = ext->mbr.mbr_parts[1];
1626 for (i = 0; i < MBR_PART_COUNT; i++) {
1627 if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) {
1628 mbrp[i].mbrp_scyl = 0;
1629 mbrp[i].mbrp_shd = 0;
1630 mbrp[i].mbrp_ssect = 0;
1631 mbrp[i].mbrp_ecyl = 0;
1632 mbrp[i].mbrp_ehd = 0;
1633 mbrp[i].mbrp_esect = 0;
1634 continue;
1636 pstart = mbrp[i].mbrp_start;
1637 psize = mbrp[i].mbrp_size;
1638 mbrp[i].mbrp_start = htole32(pstart);
1639 mbrp[i].mbrp_size = htole32(psize);
1640 if (convert) {
1641 convert_mbr_chs(bcyl, bhead, bsec,
1642 &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
1643 &mbrp[i].mbrp_ssect, pstart);
1644 convert_mbr_chs(bcyl, bhead, bsec,
1645 &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
1646 &mbrp[i].mbrp_esect, pstart + psize - 1);
1650 mbrsec.mbr_magic = htole16(MBR_MAGIC);
1651 if (pwrite(fd, &mbrsec, sizeof mbrsec,
1652 sector * (off_t)MBR_SECSIZE) < 0) {
1653 ret = -1;
1654 break;
1658 (void)close(fd);
1659 return ret;
1663 valid_mbr(struct mbr_sector *mbrs)
1666 return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
1669 static void
1670 convert_mbr_chs(int cyl, int head, int sec,
1671 uint8_t *cylp, uint8_t *headp, uint8_t *secp,
1672 uint32_t relsecs)
1674 unsigned int tcyl, temp, thead, tsec;
1676 temp = head * sec;
1677 tcyl = relsecs / temp;
1678 relsecs -= tcyl * temp;
1680 thead = relsecs / sec;
1681 tsec = relsecs - thead * sec + 1;
1683 if (tcyl > MAXCYL)
1684 tcyl = MAXCYL;
1686 *cylp = MBR_PUT_LSCYL(tcyl);
1687 *headp = thead;
1688 *secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
1692 * This function is ONLY to be used as a last resort to provide a
1693 * hint for the user. Ports should provide a more reliable way
1694 * of getting the BIOS geometry. The i386 code, for example,
1695 * uses the BIOS geometry as passed on from the bootblocks,
1696 * and only uses this as a hint to the user when that information
1697 * is not present, or a match could not be made with a NetBSD
1698 * device.
1702 guess_biosgeom_from_mbr(mbr_info_t *mbri, int *cyl, int *head, daddr_t *sec)
1704 struct mbr_sector *mbrs = &mbri->mbr;
1705 struct mbr_partition *parts = &mbrs->mbr_parts[0];
1706 int xcylinders, xheads, i, j;
1707 daddr_t xsectors;
1708 int c1, h1, s1, c2, h2, s2;
1709 daddr_t a1, a2;
1710 uint64_t num, denom;
1713 * The physical parameters may be invalid as bios geometry.
1714 * If we cannot determine the actual bios geometry, we are
1715 * better off picking a likely 'faked' geometry than leaving
1716 * the invalid physical one.
1719 xcylinders = dlcyl;
1720 xheads = dlhead;
1721 xsectors = dlsec;
1722 if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) {
1723 xsectors = MAXSECTOR;
1724 xheads = MAXHEAD;
1725 xcylinders = dlsize / (MAXSECTOR * MAXHEAD);
1726 if (xcylinders > MAXCYL)
1727 xcylinders = MAXCYL;
1729 *cyl = xcylinders;
1730 *head = xheads;
1731 *sec = xsectors;
1733 xheads = -1;
1735 /* Try to deduce the number of heads from two different mappings. */
1736 for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
1737 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1738 continue;
1739 a1 -= s1;
1740 for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
1741 if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
1742 continue;
1743 a2 -= s2;
1744 num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
1745 denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
1746 if (num != 0 && denom != 0 && num % denom == 0) {
1747 xheads = (int)(num / denom);
1748 xsectors = a1 / (c1 * xheads + h1);
1749 break;
1752 if (xheads != -1)
1753 break;
1756 if (xheads == -1)
1757 return -1;
1760 * Estimate the number of cylinders.
1761 * XXX relies on get_disks having been called.
1763 xcylinders = dlsize / xheads / xsectors;
1764 if (dlsize != xcylinders * xheads * xsectors)
1765 xcylinders++;
1768 * Now verify consistency with each of the partition table entries.
1769 * Be willing to shove cylinders up a little bit to make things work,
1770 * but translation mismatches are fatal.
1772 for (i = 0; i < MBR_PART_COUNT * 2; i++) {
1773 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
1774 continue;
1775 if (c1 >= MAXCYL - 1)
1776 /* Ignore anything that is near the CHS limit */
1777 continue;
1778 if (xsectors * (c1 * xheads + h1) + s1 != a1)
1779 return -1;
1783 * Everything checks out. Reset the geometry to use for further
1784 * calculations.
1786 *cyl = MIN(xcylinders, MAXCYL);
1787 *head = xheads;
1788 *sec = xsectors;
1789 return 0;
1792 static int
1793 get_mapping(struct mbr_partition *parts, int i,
1794 int *cylinder, int *head, int *sector, daddr_t *absolute)
1796 struct mbr_partition *apart = &parts[i / 2];
1798 if (apart->mbrp_type == 0)
1799 return -1;
1800 if (i % 2 == 0) {
1801 *cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
1802 *head = apart->mbrp_shd;
1803 *sector = MBR_PSECT(apart->mbrp_ssect) - 1;
1804 *absolute = le32toh(apart->mbrp_start);
1805 } else {
1806 *cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
1807 *head = apart->mbrp_ehd;
1808 *sector = MBR_PSECT(apart->mbrp_esect) - 1;
1809 *absolute = le32toh(apart->mbrp_start)
1810 + le32toh(apart->mbrp_size) - 1;
1812 /* Sanity check the data against max values */
1813 if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
1814 /* cannot be a CHS mapping */
1815 return -1;
1817 return 0;