1 /* $NetBSD: mbr.c,v 1.48 2024/04/11 06:42:18 andvar Exp $ */
2
3 /*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
8 *
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. The name of Piermont Information Systems Inc. may not be used to endorse
18 * or promote products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 /*
36 * Following applies to the geometry guessing code
37 */
38
39 /*
40 * Mach Operating System
41 * Copyright (c) 1992 Carnegie Mellon University
42 * All Rights Reserved.
43 *
44 * Permission to use, copy, modify and distribute this software and its
45 * documentation is hereby granted, provided that both the copyright
46 * notice and this permission notice appear in all copies of the
47 * software, derivative works or modified versions, and any portions
48 * thereof, and that both notices appear in supporting documentation.
49 *
50 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
51 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
52 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
53 *
54 * Carnegie Mellon requests users of this software to return to
55 *
56 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
57 * School of Computer Science
58 * Carnegie Mellon University
59 * Pittsburgh PA 15213-3890
60 *
61 * any improvements or extensions that they make and grant Carnegie Mellon
62 * the rights to redistribute these changes.
63 */
64
65 /* mbr.c -- DOS Master Boot Record editing code */
66
67 #ifdef HAVE_MBR
68
69 #include <sys/param.h>
70 #include <sys/types.h>
71 #include <assert.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <unistd.h>
75 #include <fcntl.h>
76 #include <util.h>
77 #include <paths.h>
78 #include <sys/ioctl.h>
79 #include "defs.h"
80 #include "mbr.h"
81 #include "md.h"
82 #include "msg_defs.h"
83 #include "menu_defs.h"
84 #include "defsizes.h"
85 #include "endian.h"
86
87 #define NO_BOOTMENU (-0x100)
88
89 #define MAXCYL 1023 /* Possibly 1024 */
90 #define MAXHEAD 255 /* Possibly 256 */
91 #define MAXSECTOR 63
92
93
94 #define MBR_UNKNOWN_PTYPE 94 /* arbitrary not widely used value */
95
96
97 /* A list of predefined partition types */
98 const struct {
99 unsigned int ptype;
100 const char *desc;
101 } mbr_part_types_src[] = {
102 { .ptype=MBR_PTYPE_NETBSD, .desc="NetBSD" },
103 { .ptype=MBR_PTYPE_FAT32L, .desc="Windows FAT32, LBA" },
104 { .ptype=MBR_PTYPE_EXT_LBA, .desc="Extended partition, LBA" },
105 { .ptype=MBR_PTYPE_386BSD, .desc="FreeBSD/386BSD" },
106 { .ptype=MBR_PTYPE_OPENBSD, .desc="OpenBSD" },
107 { .ptype=MBR_PTYPE_LNXEXT2, .desc="Linux native" },
108 { .ptype=MBR_PTYPE_LNXSWAP, .desc="Linux swap" },
109 { .ptype=MBR_PTYPE_NTFSVOL, .desc="NTFS volume set" },
110 { .ptype=MBR_PTYPE_NTFS, .desc="NTFS" },
111 { .ptype=MBR_PTYPE_PREP, .desc="PReP Boot" },
112 #ifdef MBR_PTYPE_SOLARIS
113 { .ptype=MBR_PTYPE_SOLARIS, .desc="Solaris" },
114 #endif
115 { .ptype=MBR_PTYPE_FAT12, .desc="DOS FAT12" },
116 { .ptype=MBR_PTYPE_FAT16S, .desc="DOS FAT16, <32M" },
117 { .ptype=MBR_PTYPE_FAT16B, .desc="DOS FAT16, >32M" },
118 { .ptype=MBR_PTYPE_FAT16L, .desc="Windows FAT16, LBA" },
119 { .ptype=MBR_PTYPE_FAT32, .desc="Windows FAT32" },
120 { .ptype=MBR_PTYPE_EFI, .desc="(U)EFI system partition" },
121 };
122
123 /* bookeeping of available partition types (including custom ones) */
124 struct mbr_part_type_info {
125 struct part_type_desc gen; /* generic description */
126 char desc_buf[40], short_buf[10];
127 size_t next_ptype; /* user interface order */
128 };
129
130 const struct disk_partitioning_scheme mbr_parts;
131 struct mbr_disk_partitions {
132 struct disk_partitions dp, *dlabel;
133 mbr_info_t mbr;
134 uint ptn_alignment, ptn_0_offset, ext_ptn_alignment,
135 geo_sec, geo_head, geo_cyl, target;
136 };
137
138 const struct disk_partitioning_scheme mbr_parts;
139
140 static size_t known_part_types = 0, last_added_part_type = 0;
141 static const size_t first_part_type = MBR_PTYPE_NETBSD;
142
143 /* all partition types (we are lucky, only a fixed number is possible) */
144 struct mbr_part_type_info mbr_gen_type_desc[256];
145
146 extern const struct disk_partitioning_scheme disklabel_parts;
147
148 static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *,
149 uint8_t *, uint32_t);
150
151 static part_id mbr_add_part(struct disk_partitions *arg,
152 const struct disk_part_info *info, const char **errmsg);
153
154 static size_t mbr_get_free_spaces(const struct disk_partitions *arg,
155 struct disk_part_free_space *result, size_t max_num_result,
156 daddr_t min_size, daddr_t align, daddr_t lower_bound, daddr_t ignore);
157
158 static size_t mbr_type_from_gen_desc(const struct part_type_desc *desc);
159
160 /*
161 * Notes on the extended partition editor.
162 *
163 * The extended partition structure is actually a singly linked list.
164 * Each of the 'mbr' sectors can only contain 2 items, the first describes
165 * a user partition (relative to that mbr sector), the second describes
166 * the following partition (relative to the start of the extended partition).
167 *
168 * The 'start' sector for the user partition is always the size of one
169 * track - very often 63. The extended partitions themselves should
170 * always start on a cylinder boundary using the BIOS geometry - often
171 * 16065 sectors per cylinder.
172 *
173 * The disk is also always described in increasing sector order.
174 *
175 * During editing we keep the mbr sectors accurate (it might have been
176 * easier to use absolute sector numbers though), and keep a copy of the
177 * entire sector - to preserve any information any other OS has tried
178 * to squirrel away in the (apparently) unused space.
179 *
180 * Typical disk (with some small numbers):
181 *
182 * 0 -> a 63 37 dos
183 * b 100 1000 extended LBA (type 15)
184 *
185 * 100 -> a 63 37 user
186 * b 100 200 extended partition (type 5)
187 *
188 * 200 -> a 63 37 user
189 * b 200 300 extended partition (type 5)
190 *
191 * 300 -> a 63 37 user
192 * b 0 0 0 (end of chain)
193 *
194 */
195
196 #ifndef debug_extended
197 #define dump_mbr(mbr, msg)
198 #else
199 static void
dump_mbr(mbr_info_t * m,const char * label)200 dump_mbr(mbr_info_t *m, const char *label)
201 {
202 int i;
203 uint ext_base = 0;
204
205 fprintf(stderr, "\n%s: bsec %d\n", label, bsec);
206 do {
207 fprintf(stderr, "%p: %12u %p\n",
208 m, m->sector, m->extended);
209 for (i = 0; i < MBR_PART_COUNT; i++) {
210 fprintf(stderr, " %*d %12u %12u %12" PRIu64,
211 10,
212 m->mbr.mbr_parts[i].mbrp_type,
213 m->mbr.mbr_parts[i].mbrp_start,
214 m->mbr.mbr_parts[i].mbrp_size,
215 (uint64_t)m->mbr.mbr_parts[i].mbrp_start +
216 (uint64_t)m->mbr.mbr_parts[i].mbrp_size);
217 if (m->sector == 0 &&
218 MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
219 ext_base = m->mbr.mbr_parts[i].mbrp_start;
220 if (m->sector > 0 &&
221 m->mbr.mbr_parts[i].mbrp_size > 0) {
222 uint off = MBR_IS_EXTENDED(
223 m->mbr.mbr_parts[i].mbrp_type)
224 ? ext_base : m->sector;
225 fprintf(stderr, " -> [%u .. %u]",
226 m->mbr.mbr_parts[i].mbrp_start + off,
227 m->mbr.mbr_parts[i].mbrp_size +
228 m->mbr.mbr_parts[i].mbrp_start + off);
229 }
230 fprintf(stderr, ",\n");
231 if (m->sector > 0 && i >= 1)
232 break;
233 }
234 } while ((m = m->extended));
235 }
236 #endif
237
238 /*
239 * Like pread, but handles re-blocking for non 512 byte sector disks
240 */
241 static ssize_t
blockread(int fd,size_t secsize,void * buf,size_t nbytes,off_t offset)242 blockread(int fd, size_t secsize, void *buf, size_t nbytes, off_t offset)
243 {
244 ssize_t nr;
245 off_t sector = offset / 512;
246 off_t offs = sector * (off_t)secsize;
247 off_t mod = offs & (secsize - 1);
248 off_t rnd = offs & ~(secsize - 1);
249 char *iobuf;
250
251 assert(nbytes <= 512);
252
253 if (secsize == 512)
254 return pread(fd, buf, nbytes, offset);
255
256 iobuf = malloc(secsize);
257 if (iobuf == NULL)
258 return -1;
259 nr = pread(fd, iobuf, secsize, rnd);
260 if (nr == (off_t)secsize)
261 memcpy(buf, &iobuf[mod], nbytes);
262 free(iobuf);
263
264 return nr == (off_t)secsize ? (off_t)nbytes : -1;
265 }
266
267 /*
268 * Same for pwrite
269 */
270 static ssize_t
blockwrite(int fd,size_t secsize,const void * buf,size_t nbytes,off_t offset)271 blockwrite(int fd, size_t secsize, const void *buf, size_t nbytes,
272 off_t offset)
273 {
274 ssize_t nr;
275 off_t sector = offset / secsize;
276 off_t offs = sector * (off_t)secsize;
277 off_t mod = offs & (secsize - 1);
278 off_t rnd = offs & ~(secsize - 1);
279 char *iobuf;
280
281 assert(nbytes <= 512);
282
283 if (secsize == 512)
284 return pwrite(fd, buf, nbytes, offset);
285
286 iobuf = malloc(secsize);
287 if (iobuf == NULL)
288 return -1;
289 nr = pread(fd, iobuf, secsize, rnd);
290 if (nr == (off_t)secsize) {
291 memcpy(&iobuf[mod], buf, nbytes);
292 nr = pwrite(fd, iobuf, secsize, rnd);
293 }
294 free(iobuf);
295
296 return nr == (off_t)secsize ? (off_t)nbytes : -1;
297 }
298
299 static void
free_last_mounted(mbr_info_t * m)300 free_last_mounted(mbr_info_t *m)
301 {
302 size_t i;
303
304 for (i = 0; i < MBR_PART_COUNT; i++)
305 free(__UNCONST(m->last_mounted[i]));
306 }
307
308 static void
free_mbr_info(mbr_info_t * m)309 free_mbr_info(mbr_info_t *m)
310 {
311 if (m == NULL)
312 return;
313 free_last_mounted(m);
314 free(m);
315 }
316
317 /*
318 * To be used only on ports which cannot provide any bios geometry
319 */
320 int
set_bios_geom_with_mbr_guess(struct disk_partitions * parts)321 set_bios_geom_with_mbr_guess(struct disk_partitions *parts)
322 {
323 int cyl, head, sec;
324
325 msg_fmt_display(MSG_nobiosgeom, "%d%d%d",
326 pm->dlcyl, pm->dlsec, pm->dlhead);
327 if (guess_biosgeom_from_parts(parts, &cyl, &head, &sec) >= 0)
328 msg_fmt_display_add(MSG_biosguess, "%d%d%d", cyl, head, sec);
329 set_bios_geom(parts, &cyl, &head, &sec);
330 if (parts->pscheme->change_disk_geom)
331 parts->pscheme->change_disk_geom(parts, cyl, head, sec);
332
333 return edit_outer_parts(parts);
334 }
335
336 static void
mbr_init_chs(struct mbr_disk_partitions * parts,int ncyl,int nhead,int nsec)337 mbr_init_chs(struct mbr_disk_partitions *parts, int ncyl, int nhead, int nsec)
338 {
339 if (ncyl > MAXCYL)
340 ncyl = MAXCYL;
341 pm->current_cylsize = nhead*nsec;
342 pm->max_chs = (unsigned long)ncyl*nhead*nsec;
343 parts->geo_sec = nsec;
344 parts->geo_head = nhead;
345 parts->geo_cyl = ncyl;
346 }
347
348 /*
349 * get C/H/S geometry from user via menu interface and
350 * store in globals.
351 */
352 void
set_bios_geom(struct disk_partitions * parts,int * cyl,int * head,int * sec)353 set_bios_geom(struct disk_partitions *parts, int *cyl, int *head, int *sec)
354 {
355 char res[80];
356 int bsec, bhead, bcyl;
357 daddr_t s;
358
359 if (parts->pscheme->change_disk_geom == NULL)
360 return;
361
362 msg_display_add(MSG_setbiosgeom);
363
364 do {
365 snprintf(res, 80, "%d", *sec);
366 msg_prompt_add(MSG_sectors, res, res, 80);
367 bsec = atoi(res);
368 } while (bsec <= 0 || bsec > MAXSECTOR);
369
370 do {
371 snprintf(res, 80, "%d", *head);
372 msg_prompt_add(MSG_heads, res, res, 80);
373 bhead = atoi(res);
374 } while (bhead <= 0 || bhead > MAXHEAD);
375 s = min(pm->dlsize, mbr_parts.size_limit);
376 bcyl = s / bsec / bhead;
377 if (s != bcyl * bsec * bhead)
378 bcyl++;
379 if (bcyl > MAXCYL)
380 bcyl = MAXCYL;
381 pm->max_chs = (unsigned long)bcyl * bhead * bsec;
382 pm->current_cylsize = bhead * bsec;
383 parts->pscheme->change_disk_geom(parts, bcyl, bhead, bsec);
384 *cyl = bcyl;
385 *head = bhead;
386 *sec = bsec;
387 }
388
389 static int
find_mbr_space(const struct mbr_info_t * mbrs,uint * start,uint * size,uint from,uint end_of_disk,uint ignore_at,bool primary_only)390 find_mbr_space(const struct mbr_info_t *mbrs, uint *start, uint *size,
391 uint from, uint end_of_disk, uint ignore_at, bool primary_only)
392 {
393 uint sz;
394 int i, j;
395 uint s, e;
396 const mbr_info_t *m, *me;
397 bool is_extended;
398
399 check_again:
400 m = mbrs;
401 sz = end_of_disk-from;
402 if (from >= end_of_disk)
403 return -1;
404
405 for (i = 0; i < MBR_PART_COUNT; i++) {
406 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
407 continue;
408
409 is_extended = MBR_IS_EXTENDED(
410 m->mbr.mbr_parts[i].mbrp_type);
411
412 s = m->mbr.mbr_parts[i].mbrp_start + m->sector;
413 if (s == ignore_at)
414 continue;
415 e = s + m->mbr.mbr_parts[i].mbrp_size;
416 if (s <= from && e > from
417 && (!is_extended || primary_only)) {
418 if (e - 1 >= end_of_disk)
419 break;
420 if (e >= UINT_MAX) {
421 sz = 0;
422 break;
423 }
424 from = e + 1;
425 goto check_again;
426 }
427 if (s <= from && e > from && is_extended) {
428 /*
429 * if we start in the extended partition,
430 * we must end before its end
431 */
432 sz = e - from;
433 }
434 if (s > from && s - from < sz)
435 sz = s - from;
436
437 if (is_extended) {
438 for (me = m->extended; me != NULL; me = me->extended) {
439 for (j = 0; j < MBR_PART_COUNT; j++) {
440 if (me->mbr.mbr_parts[j].mbrp_type ==
441 MBR_PTYPE_UNUSED)
442 continue;
443
444 is_extended = MBR_IS_EXTENDED(
445 me->mbr.mbr_parts[j].mbrp_type);
446
447 if (is_extended && i > 0)
448 break;
449
450 s = me->mbr.mbr_parts[j].mbrp_start +
451 me->sector;
452 if (s == ignore_at)
453 continue;
454 e = s + me->mbr.mbr_parts[j].mbrp_size;
455 if (me->sector != 0 && me->sector< s)
456 /*
457 * can not allow to overwrite
458 * the ext mbr
459 */
460 s = me->sector;
461 if (s <= from && e > from) {
462 if (e - 1 >= end_of_disk)
463 break;
464 from = e + 1;
465 goto check_again;
466 }
467 if (s > from && s - from < sz)
468 sz = s - from;
469
470 }
471 }
472 }
473 }
474
475 if (sz == 0)
476 return -1;
477 if (start != NULL)
478 *start = from;
479 if (size != NULL)
480 *size = sz;
481 return 0;
482 }
483
484 #ifdef BOOTSEL
485 static int
validate_and_set_names(mbr_info_t * mbri,const struct mbr_bootsel * src,uint32_t ext_base)486 validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src,
487 uint32_t ext_base)
488 {
489 size_t i, l;
490 const unsigned char *p;
491
492 /*
493 * The 16 bit magic used to detect whether mbr_bootsel is valid
494 * or not is pretty week - collisions have been seen in the wild;
495 * but maybe it is just foreign tools corruption reminiscents
496 * of NetBSD MBRs. Anyway, before accepting a boot menu definition,
497 * make sure it is kinda "sane".
498 */
499
500 for (i = 0; i < MBR_PART_COUNT; i++) {
501 /*
502 * Make sure the name does not contain control chars
503 * (not using iscntrl due to minimalistic locale support
504 * in miniroot environments) and is properly 0-terminated.
505 */
506 for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i];
507 *p != 0; l++, p++) {
508 if (l > MBR_BS_PARTNAMESIZE)
509 return 0;
510 if (*p < ' ') /* hacky 'iscntrl' */
511 return 0;
512 }
513 }
514
515 memcpy(&mbri->mbrb, src, sizeof(*src));
516
517 if (ext_base == 0)
518 return mbri->mbrb.mbrbs_defkey - SCAN_1;
519 return 0;
520 }
521 #endif
522
523 static int
valid_mbr(struct mbr_sector * mbrs)524 valid_mbr(struct mbr_sector *mbrs)
525 {
526
527 return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
528 }
529
530 static int
read_mbr(const char * disk,size_t secsize,mbr_info_t * mbri,struct mbr_disk_partitions * parts)531 read_mbr(const char *disk, size_t secsize, mbr_info_t *mbri,
532 struct mbr_disk_partitions *parts)
533 {
534 struct mbr_partition *mbrp;
535 struct mbr_sector *mbrs = &mbri->mbr;
536 mbr_info_t *ext = NULL;
537 char diskpath[MAXPATHLEN];
538 int fd, i;
539 uint32_t ext_base = 0, next_ext = 0;
540 int rval = -1;
541 #ifdef BOOTSEL
542 mbr_info_t *ombri = mbri;
543 int bootkey = 0;
544 #endif
545
546 memset(mbri, 0, sizeof *mbri);
547
548 /* Open the disk. */
549 fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
550 if (fd < 0)
551 goto bad_mbr;
552
553 for (;;) {
554 if (blockread(fd, secsize, mbrs, sizeof *mbrs,
555 (ext_base + next_ext) * (off_t)MBR_SECSIZE)
556 - sizeof *mbrs != 0)
557 break;
558
559 if (!valid_mbr(mbrs))
560 break;
561
562 mbrp = &mbrs->mbr_parts[0];
563 if (ext_base != 0) {
564 /* sanity check extended chain */
565 if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
566 break;
567 if (mbrp[1].mbrp_type != MBR_PTYPE_UNUSED &&
568 !MBR_IS_EXTENDED(mbrp[1].mbrp_type))
569 break;
570 if (mbrp[2].mbrp_type != MBR_PTYPE_UNUSED
571 || mbrp[3].mbrp_type != MBR_PTYPE_UNUSED)
572 break;
573 /* Looks ok, link into extended chain */
574 mbri->extended = ext;
575 ext->extended = NULL;
576 mbri = ext;
577 ext = NULL;
578 }
579 #if BOOTSEL
580 if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
581 /* old bootsel, grab bootsel info */
582 bootkey = validate_and_set_names(mbri,
583 (struct mbr_bootsel *)
584 ((uint8_t *)mbrs + MBR_BS_OLD_OFFSET),
585 ext_base);
586 } else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
587 /* new location */
588 bootkey = validate_and_set_names(mbri,
589 &mbrs->mbr_bootsel, ext_base);
590 }
591 /* Save original flags for mbr code update tests */
592 mbri->oflags = mbri->mbrb.mbrbs_flags;
593 #endif
594 mbri->sector = next_ext + ext_base;
595 next_ext = 0;
596 rval = 0;
597 for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
598 if (mbrp->mbrp_type == MBR_PTYPE_UNUSED) {
599 /* type is unused, discard scum */
600 memset(mbrp, 0, sizeof *mbrp);
601 continue;
602 }
603 mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
604 mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
605 if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
606 next_ext = mbrp->mbrp_start;
607 } else {
608 uint flags = 0;
609 if (mbrp->mbrp_type == MBR_PTYPE_NETBSD) {
610 flags |= GLM_LIKELY_FFS;
611 if (parts->target == ~0U)
612 parts->target =
613 mbri->sector +
614 mbrp->mbrp_start;
615 } else if (mbrp->mbrp_type == MBR_PTYPE_FAT12 ||
616 mbrp->mbrp_type == MBR_PTYPE_FAT16S ||
617 mbrp->mbrp_type == MBR_PTYPE_FAT16B ||
618 mbrp->mbrp_type == MBR_PTYPE_FAT32 ||
619 mbrp->mbrp_type == MBR_PTYPE_FAT32L ||
620 mbrp->mbrp_type == MBR_PTYPE_FAT16L ||
621 mbrp->mbrp_type == MBR_PTYPE_EFI)
622 flags |= GLM_MAYBE_FAT32;
623 else if (mbrp->mbrp_type == MBR_PTYPE_NTFS)
624 flags |= GLM_MAYBE_NTFS;
625 if (flags != 0) {
626 const char *mount = get_last_mounted(
627 fd, mbri->sector + mbrp->mbrp_start,
628 &mbri->fs_type[i],
629 &mbri->fs_sub_type[i],
630 flags);
631 char *p = strdup(mount);
632 canonicalize_last_mounted(p);
633 mbri->last_mounted[i] = p;
634 }
635 }
636 #if BOOTSEL
637 if (mbri->mbrb.mbrbs_nametab[i][0] != 0
638 && bootkey-- == 0)
639 ombri->bootsec = mbri->sector +
640 mbrp->mbrp_start;
641 #endif
642 }
643
644 if (next_ext == 0 || ext_base + next_ext <= mbri->sector)
645 break;
646 if (ext_base == 0) {
647 ext_base = next_ext;
648 next_ext = 0;
649 }
650 ext = calloc(1, sizeof *ext);
651 if (!ext)
652 break;
653 mbrs = &ext->mbr;
654 }
655
656 bad_mbr:
657 free_mbr_info(ext);
658 if (fd >= 0)
659 close(fd);
660 if (rval == -1) {
661 memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts);
662 mbrs->mbr_magic = htole16(MBR_MAGIC);
663 }
664 dump_mbr(ombri, "read");
665 return rval;
666 }
667
668 static int
write_mbr(const char * disk,size_t secsize,mbr_info_t * mbri,int bsec,int bhead,int bcyl)669 write_mbr(const char *disk, size_t secsize, mbr_info_t *mbri, int bsec,
670 int bhead, int bcyl)
671 {
672 char diskpath[MAXPATHLEN];
673 int fd, i, ret = 0, bits = 0;
674 struct mbr_partition *mbrp;
675 u_int32_t pstart, psize;
676 #ifdef BOOTSEL
677 struct mbr_sector *mbrs;
678 #endif
679 struct mbr_sector mbrsec;
680 mbr_info_t *ext;
681 uint sector;
682
683 dump_mbr(mbri, "write");
684
685 /* Open the disk. */
686 fd = opendisk(disk, secsize == 512 ? O_WRONLY : O_RDWR,
687 diskpath, sizeof(diskpath), 0);
688 if (fd < 0)
689 return -1;
690
691 /* Remove all wedges */
692 if (ioctl(fd, DIOCRMWEDGES, &bits) == -1)
693 return -1;
694
695 #ifdef BOOTSEL
696 /*
697 * If the main boot code (appears to) contain the netbsd bootcode,
698 * copy in all the menu strings and set the default keycode
699 * to be that for the default partition.
700 * Unfortunately we can't rely on the user having actually updated
701 * to the new mbr code :-(
702 */
703 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC)
704 || mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) {
705 int8_t key = SCAN_1;
706 uint offset = MBR_BS_OFFSET;
707 if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC))
708 offset = MBR_BS_OLD_OFFSET;
709 mbri->mbrb.mbrbs_defkey = SCAN_ENTER;
710 if (mbri->mbrb.mbrbs_timeo == 0)
711 mbri->mbrb.mbrbs_timeo = 182; /* 10 seconds */
712 for (ext = mbri; ext != NULL; ext = ext->extended) {
713 mbrs = &ext->mbr;
714 mbrp = &mbrs->mbr_parts[0];
715 /* Ensure marker is set in each sector */
716 mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic;
717 /* and copy in bootsel parameters */
718 *(struct mbr_bootsel *)((uint8_t *)mbrs + offset) =
719 ext->mbrb;
720 for (i = 0; i < MBR_PART_COUNT; i++) {
721 if (ext->mbrb.mbrbs_nametab[i][0] == 0)
722 continue;
723 if (ext->sector + mbrp->mbrp_start ==
724 mbri->bootsec)
725 mbri->mbrb.mbrbs_defkey = key;
726 key++;
727 }
728 }
729 /* copy main data (again) since we've put the 'key' in */
730 *(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) =
731 mbri->mbrb;
732 }
733 #endif
734
735 for (ext = mbri; ext != NULL; ext = ext->extended) {
736 memset(mbri->wedge, 0, sizeof mbri->wedge);
737 sector = ext->sector;
738 mbrsec = ext->mbr; /* copy sector */
739 mbrp = &mbrsec.mbr_parts[0];
740
741 if (sector != 0 && ext->extended != NULL
742 && ext->extended->mbr.mbr_parts[0].mbrp_type
743 == MBR_PTYPE_UNUSED) {
744
745 /*
746 * This should never happen nowadays, old code
747 * inserted empty ext sectors in the chain to
748 * help the gui out - we do not do that anymore.
749 */
750 assert(false);
751
752 /* We are followed by an empty slot, collapse out */
753 ext = ext->extended;
754 /* Make us describe the next non-empty partition */
755 mbrp[1] = ext->mbr.mbr_parts[1];
756 }
757
758 for (i = 0; i < MBR_PART_COUNT; i++) {
759 if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) {
760 mbrp[i].mbrp_scyl = 0;
761 mbrp[i].mbrp_shd = 0;
762 mbrp[i].mbrp_ssect = 0;
763 mbrp[i].mbrp_ecyl = 0;
764 mbrp[i].mbrp_ehd = 0;
765 mbrp[i].mbrp_esect = 0;
766 continue;
767 }
768 pstart = mbrp[i].mbrp_start;
769 psize = mbrp[i].mbrp_size;
770 mbrp[i].mbrp_start = htole32(pstart);
771 mbrp[i].mbrp_size = htole32(psize);
772 if (bsec && bcyl && bhead) {
773 convert_mbr_chs(bcyl, bhead, bsec,
774 &mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
775 &mbrp[i].mbrp_ssect, pstart);
776 convert_mbr_chs(bcyl, bhead, bsec,
777 &mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
778 &mbrp[i].mbrp_esect, pstart + psize - 1);
779 }
780 }
781
782 mbrsec.mbr_magic = htole16(MBR_MAGIC);
783 if (blockwrite(fd, secsize, &mbrsec, sizeof mbrsec,
784 sector * (off_t)MBR_SECSIZE) < 0) {
785 ret = -1;
786 break;
787 }
788 }
789
790 (void)close(fd);
791 return ret;
792 }
793
794 static void
convert_mbr_chs(int cyl,int head,int sec,uint8_t * cylp,uint8_t * headp,uint8_t * secp,uint32_t relsecs)795 convert_mbr_chs(int cyl, int head, int sec,
796 uint8_t *cylp, uint8_t *headp, uint8_t *secp,
797 uint32_t relsecs)
798 {
799 unsigned int tcyl, temp, thead, tsec;
800
801 temp = head * sec;
802 tcyl = relsecs / temp;
803 relsecs -= tcyl * temp;
804
805 thead = relsecs / sec;
806 tsec = relsecs - thead * sec + 1;
807
808 if (tcyl > MAXCYL)
809 tcyl = MAXCYL;
810
811 *cylp = MBR_PUT_LSCYL(tcyl);
812 *headp = thead;
813 *secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
814 }
815
816 /*
817 * This function is ONLY to be used as a last resort to provide a
818 * hint for the user. Ports should provide a more reliable way
819 * of getting the BIOS geometry. The i386 code, for example,
820 * uses the BIOS geometry as passed on from the bootblocks,
821 * and only uses this as a hint to the user when that information
822 * is not present, or a match could not be made with a NetBSD
823 * device.
824 */
825 int
guess_biosgeom_from_parts(struct disk_partitions * parts,int * cyl,int * head,int * sec)826 guess_biosgeom_from_parts(struct disk_partitions *parts,
827 int *cyl, int *head, int *sec)
828 {
829
830 /*
831 * The physical parameters may be invalid as bios geometry.
832 * If we cannot determine the actual bios geometry, we are
833 * better off picking a likely 'faked' geometry than leaving
834 * the invalid physical one.
835 */
836
837 int xcylinders = pm->dlcyl;
838 int xheads = pm->dlhead;
839 daddr_t xsectors = pm->dlsec;
840 daddr_t xsize = min(pm->dlsize, mbr_parts.size_limit);
841 if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) {
842 xsectors = MAXSECTOR;
843 xheads = MAXHEAD;
844 xcylinders = xsize / (MAXSECTOR * MAXHEAD);
845 if (xcylinders > MAXCYL)
846 xcylinders = MAXCYL;
847 }
848 *cyl = xcylinders;
849 *head = xheads;
850 *sec = xsectors;
851
852 if (parts->pscheme->guess_disk_geom == NULL)
853 return -1;
854
855 return parts->pscheme->guess_disk_geom(parts, cyl, head, sec);
856 }
857
858 static int
mbr_comp_part_entry(const void * a,const void * b)859 mbr_comp_part_entry(const void *a, const void *b)
860 {
861 const struct mbr_partition *part_a = a,
862 *part_b = b;
863
864 if (part_a->mbrp_type == MBR_PTYPE_UNUSED
865 && part_b->mbrp_type != MBR_PTYPE_UNUSED)
866 return 1;
867
868 if (part_b->mbrp_type == MBR_PTYPE_UNUSED
869 && part_a->mbrp_type != MBR_PTYPE_UNUSED)
870 return -1;
871
872 return part_a->mbrp_start < part_b->mbrp_start ? -1 : 1;
873 }
874
875 static void
mbr_sort_main_mbr(struct mbr_sector * m)876 mbr_sort_main_mbr(struct mbr_sector *m)
877 {
878 qsort(&m->mbr_parts[0], MBR_PART_COUNT,
879 sizeof(m->mbr_parts[0]), mbr_comp_part_entry);
880 }
881
882 static void
mbr_init_default_alignments(struct mbr_disk_partitions * parts,uint track)883 mbr_init_default_alignments(struct mbr_disk_partitions *parts, uint track)
884 {
885 if (track == 0)
886 track = 16065;
887
888 assert(parts->dp.disk_size > 0);
889 if (parts->dp.disk_size < 0)
890 return;
891
892 parts->ptn_0_offset = parts->geo_sec;
893
894 /* Use 1MB offset/alignment for large (>128GB) disks */
895 if (parts->dp.disk_size > HUGE_DISK_SIZE) {
896 parts->ptn_alignment = 2048;
897 } else if (parts->dp.disk_size > TINY_DISK_SIZE) {
898 parts->ptn_alignment = 64;
899 } else {
900 parts->ptn_alignment = 1;
901 }
902 parts->ext_ptn_alignment = track;
903 }
904
905 static struct disk_partitions *
mbr_create_new(const char * disk,daddr_t start,daddr_t len,bool is_boot_drive,struct disk_partitions * parent)906 mbr_create_new(const char *disk, daddr_t start, daddr_t len,
907 bool is_boot_drive, struct disk_partitions *parent)
908 {
909 struct mbr_disk_partitions *parts;
910 struct disk_geom geo;
911
912 assert(start == 0);
913 if (start != 0)
914 return NULL;
915
916 parts = calloc(1, sizeof(*parts));
917 if (!parts)
918 return NULL;
919
920 parts->dp.pscheme = &mbr_parts;
921 parts->dp.disk = strdup(disk);
922 if (len > mbr_parts.size_limit)
923 len = mbr_parts.size_limit;
924 parts->dp.disk_start = start;
925 parts->dp.disk_size = len;
926 parts->dp.free_space = len-1;
927 parts->dp.bytes_per_sector = 512;
928 parts->geo_sec = MAXSECTOR;
929 parts->geo_head = MAXHEAD;
930 parts->geo_cyl = len/MAXHEAD/MAXSECTOR+1;
931 parts->target = ~0U;
932
933 if (get_disk_geom(disk, &geo)) {
934 parts->geo_sec = geo.dg_nsectors;
935 parts->geo_head = geo.dg_ntracks;
936 parts->geo_cyl = geo.dg_ncylinders;
937 parts->dp.bytes_per_sector = geo.dg_secsize;
938 }
939
940 mbr_init_default_alignments(parts, 0);
941
942 return &parts->dp;
943 }
944
945 static void
mbr_calc_free_space(struct mbr_disk_partitions * parts)946 mbr_calc_free_space(struct mbr_disk_partitions *parts)
947 {
948 size_t i;
949 mbr_info_t *m;
950
951 parts->dp.free_space = parts->dp.disk_size - 1;
952 parts->dp.num_part = 0;
953 m = &parts->mbr;
954 do {
955 for (i = 0; i < MBR_PART_COUNT; i++) {
956 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
957 continue;
958
959 if (m != &parts->mbr && i > 0 &&
960 MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
961 break;
962
963 parts->dp.num_part++;
964 if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
965 continue;
966
967 daddr_t psize = m->mbr.mbr_parts[i].mbrp_size;
968 if (m != &parts->mbr)
969 psize += m->mbr.mbr_parts[i].mbrp_start;
970
971 if (psize > parts->dp.free_space)
972 parts->dp.free_space = 0;
973 else
974 parts->dp.free_space -= psize;
975 }
976 } while ((m = m->extended));
977 }
978
979 static struct disk_partitions *
mbr_read_from_disk(const char * disk,daddr_t start,daddr_t len,size_t bps,const struct disk_partitioning_scheme * scheme)980 mbr_read_from_disk(const char *disk, daddr_t start, daddr_t len, size_t bps,
981 const struct disk_partitioning_scheme *scheme)
982 {
983 struct mbr_disk_partitions *parts;
984
985 assert(start == 0);
986 if (start != 0)
987 return NULL;
988
989 parts = calloc(1, sizeof(*parts));
990 if (!parts)
991 return NULL;
992
993 parts->dp.pscheme = scheme;
994 parts->dp.disk = strdup(disk);
995 if (len >= mbr_parts.size_limit)
996 len = mbr_parts.size_limit;
997 parts->dp.disk_start = start;
998 parts->dp.disk_size = len;
999 parts->geo_sec = MAXSECTOR;
1000 parts->geo_head = MAXHEAD;
1001 parts->geo_cyl = len/MAXHEAD/MAXSECTOR+1;
1002 parts->dp.bytes_per_sector = bps;
1003 parts->target = ~0U;
1004 mbr_init_default_alignments(parts, 0);
1005 if (read_mbr(disk, parts->dp.bytes_per_sector, &parts->mbr, parts)
1006 == -1) {
1007 free(parts);
1008 return NULL;
1009 }
1010 mbr_calc_free_space(parts);
1011 if (parts->dp.num_part == 1 &&
1012 parts->dp.free_space < parts->ptn_alignment) {
1013 struct disk_part_info info;
1014
1015 /*
1016 * Check if this is a GPT protective MBR
1017 */
1018 if (parts->dp.pscheme->get_part_info(&parts->dp, 0, &info)
1019 && info.nat_type != NULL
1020 && mbr_type_from_gen_desc(info.nat_type) == 0xEE) {
1021 parts->dp.pscheme->free(&parts->dp);
1022 return NULL;
1023 }
1024 }
1025
1026 return &parts->dp;
1027 }
1028
1029 static bool
mbr_write_to_disk(struct disk_partitions * new_state)1030 mbr_write_to_disk(struct disk_partitions *new_state)
1031 {
1032 struct mbr_disk_partitions *parts =
1033 (struct mbr_disk_partitions *)new_state;
1034 unsigned long bsec, bhead, bcyl;
1035 daddr_t t;
1036
1037 assert(parts->geo_sec != 0);
1038 if (parts->geo_sec != 0) {
1039 bsec = parts->geo_sec;
1040 bhead = parts->ext_ptn_alignment / bsec;
1041 } else {
1042 bsec = MAXSECTOR;
1043 bhead = MAXHEAD;
1044 }
1045 t = bsec * bhead;
1046 assert(t != 0);
1047 if ((daddr_t)(1UL<<10) * t <= parts->dp.disk_size)
1048 bcyl = (1UL<<10) - 1;
1049 else
1050 bcyl = (unsigned long)(parts->dp.disk_size / t);
1051
1052 return write_mbr(parts->dp.disk, parts->dp.bytes_per_sector,
1053 &parts->mbr, bsec, bhead, bcyl) == 0;
1054 }
1055
1056 static bool
mbr_change_disk_geom(struct disk_partitions * arg,int ncyl,int nhead,int nsec)1057 mbr_change_disk_geom(struct disk_partitions *arg, int ncyl, int nhead,
1058 int nsec)
1059 {
1060 struct mbr_disk_partitions *parts = (struct mbr_disk_partitions *)arg;
1061 daddr_t ptn_0_base, ptn_0_limit;
1062 struct disk_part_info info;
1063
1064 /* Default to using 'traditional' cylinder alignment */
1065 mbr_init_chs(parts, ncyl, nhead, nsec);
1066 mbr_init_default_alignments(parts, nhead * nsec);
1067
1068 if (parts->dp.disk_size <= TINY_DISK_SIZE) {
1069 set_default_sizemult(arg->disk,
1070 parts->dp.bytes_per_sector, parts->dp.bytes_per_sector);
1071 return true;
1072 }
1073
1074 if (parts->dp.num_part > 0 &&
1075 parts->dp.pscheme->get_part_info(arg, 0, &info)) {
1076
1077 /* Try to copy offset of first partition */
1078 ptn_0_base = info.start;
1079 ptn_0_limit = info.start + info.size;
1080 if (!(ptn_0_limit & 2047)) {
1081 /* Partition ends on a 1MB boundary, align to 1MB */
1082 parts->ptn_alignment = 2048;
1083 if ((ptn_0_base <= 2048
1084 && !(ptn_0_base & (ptn_0_base - 1)))
1085 || (ptn_0_base < parts->ptn_0_offset)) {
1086 /*
1087 * If ptn_base is a power of 2, use it.
1088 * Also use it if the first partition
1089 * already is close to the beginning
1090 * of the disk and we can't enforce
1091 * better alignment.
1092 */
1093 parts->ptn_0_offset = ptn_0_base;
1094 }
1095 }
1096 }
1097 set_default_sizemult(arg->disk, MEG, parts->dp.bytes_per_sector);
1098 return true;
1099 }
1100
1101 static size_t
mbr_type_from_gen_desc(const struct part_type_desc * desc)1102 mbr_type_from_gen_desc(const struct part_type_desc *desc)
1103 {
1104 for (size_t i = 0; i < __arraycount(mbr_gen_type_desc); i++)
1105 if (&mbr_gen_type_desc[i].gen == desc)
1106 return i;
1107
1108 return SIZE_T_MAX;
1109 }
1110
1111 static enum part_type
mbr_map_part_type(unsigned int t)1112 mbr_map_part_type(unsigned int t)
1113 {
1114 /* Map some special MBR partition types */
1115 switch (t) {
1116 case MBR_PTYPE_FAT32:
1117 case MBR_PTYPE_FAT32L:
1118 case MBR_PTYPE_FAT16S:
1119 case MBR_PTYPE_FAT16B:
1120 case MBR_PTYPE_FAT16L:
1121 case MBR_PTYPE_FAT12:
1122 case MBR_PTYPE_FT_FAT32:
1123 case MBR_PTYPE_FT_FAT32_EXT:
1124 return PT_FAT;
1125 case MBR_PTYPE_EFI:
1126 return PT_EFI_SYSTEM;
1127 case MBR_PTYPE_LNXEXT2:
1128 return PT_EXT2;
1129 case MBR_PTYPE_NETBSD:
1130 return PT_root;
1131 }
1132
1133 return PT_unknown;
1134 }
1135
1136 static void
map_mbr_part_types(void)1137 map_mbr_part_types(void)
1138 {
1139
1140 for (size_t i = 0; i < __arraycount(mbr_part_types_src); i++) {
1141 unsigned int v = mbr_part_types_src[i].ptype;
1142
1143 snprintf(mbr_gen_type_desc[v].short_buf,
1144 sizeof(mbr_gen_type_desc[v].short_buf), "%u", v);
1145 mbr_gen_type_desc[v].gen.short_desc =
1146 mbr_gen_type_desc[v].short_buf;
1147 mbr_gen_type_desc[v].gen.description =
1148 mbr_part_types_src[i].desc;
1149 mbr_gen_type_desc[v].gen.generic_ptype = mbr_map_part_type(v);
1150 mbr_gen_type_desc[v].next_ptype = ~0U;
1151 mbr_gen_type_desc[last_added_part_type].next_ptype = v;
1152 known_part_types++;
1153 last_added_part_type = v;
1154 }
1155 }
1156
1157 static size_t
mbr_get_part_type_count(void)1158 mbr_get_part_type_count(void)
1159 {
1160 if (known_part_types == 0)
1161 map_mbr_part_types();
1162
1163 return known_part_types;
1164 }
1165
1166 static const struct part_type_desc *
mbr_get_fs_part_type(enum part_type pt,unsigned fs_type,unsigned sub_type)1167 mbr_get_fs_part_type(enum part_type pt, unsigned fs_type, unsigned sub_type)
1168 {
1169 if (known_part_types == 0)
1170 map_mbr_part_types();
1171
1172 switch (fs_type) {
1173 case FS_BSDFFS:
1174 return &mbr_gen_type_desc[MBR_PTYPE_NETBSD].gen;
1175 case FS_EX2FS:
1176 return &mbr_gen_type_desc[MBR_PTYPE_LNXEXT2].gen;
1177 case FS_MSDOS:
1178 if (sub_type == 0)
1179 sub_type = MBR_PTYPE_FAT32L;
1180
1181 switch (sub_type) {
1182 case MBR_PTYPE_FAT12:
1183 case MBR_PTYPE_FAT16S:
1184 case MBR_PTYPE_FAT16B:
1185 case MBR_PTYPE_FAT32:
1186 case MBR_PTYPE_FAT32L:
1187 case MBR_PTYPE_FAT16L:
1188 return &mbr_gen_type_desc[sub_type].gen;
1189 }
1190 break;
1191 case FS_EFI_SP:
1192 return &mbr_gen_type_desc[MBR_PTYPE_EFI].gen;
1193 }
1194
1195 return NULL;
1196 }
1197
1198 static const struct part_type_desc *
mbr_get_part_type(size_t index)1199 mbr_get_part_type(size_t index)
1200 {
1201 size_t i, no;
1202
1203 if (known_part_types == 0)
1204 map_mbr_part_types();
1205
1206 if (index >= known_part_types)
1207 return NULL;
1208
1209 for (i = first_part_type, no = 0; i < __arraycount(mbr_gen_type_desc)
1210 && no != index; no++)
1211 i = mbr_gen_type_desc[i].next_ptype;
1212
1213 if (i >= __arraycount(mbr_gen_type_desc))
1214 return NULL;
1215 return &mbr_gen_type_desc[i].gen;
1216 }
1217
1218 static const struct part_type_desc *
mbr_new_custom_part_type(unsigned int v)1219 mbr_new_custom_part_type(unsigned int v)
1220 {
1221 snprintf(mbr_gen_type_desc[v].short_buf,
1222 sizeof(mbr_gen_type_desc[v].short_buf), "%u", v);
1223 snprintf(mbr_gen_type_desc[v].desc_buf,
1224 sizeof(mbr_gen_type_desc[v].desc_buf), "%s (%u)",
1225 msg_string(MSG_custom_type), v);
1226 mbr_gen_type_desc[v].gen.short_desc = mbr_gen_type_desc[v].short_buf;
1227 mbr_gen_type_desc[v].gen.description = mbr_gen_type_desc[v].desc_buf;
1228 mbr_gen_type_desc[v].gen.generic_ptype = mbr_map_part_type(v);
1229 mbr_gen_type_desc[v].next_ptype = ~0U;
1230 mbr_gen_type_desc[last_added_part_type].next_ptype = v;
1231 known_part_types++;
1232 last_added_part_type = v;
1233
1234 return &mbr_gen_type_desc[v].gen;
1235 }
1236
1237 static const struct part_type_desc *
mbr_custom_part_type(const char * custom,const char ** err_msg)1238 mbr_custom_part_type(const char *custom, const char **err_msg)
1239 {
1240 unsigned long v;
1241 char *endp;
1242
1243 if (known_part_types == 0)
1244 map_mbr_part_types();
1245
1246 v = strtoul(custom, &endp, 10);
1247 if (v > 255 || (v == 0 && *endp != 0)) {
1248 if (err_msg != NULL)
1249 *err_msg = msg_string(MSG_mbr_type_invalid);
1250 return NULL;
1251 }
1252
1253 if (mbr_gen_type_desc[v].gen.short_desc != NULL)
1254 return &mbr_gen_type_desc[v].gen;
1255
1256 return mbr_new_custom_part_type(v);
1257 }
1258
1259 static const struct part_type_desc *
mbr_create_unknown_part_type(void)1260 mbr_create_unknown_part_type(void)
1261 {
1262
1263 if (mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen.short_desc != NULL)
1264 return &mbr_gen_type_desc[MBR_UNKNOWN_PTYPE].gen;
1265
1266 return mbr_new_custom_part_type(MBR_UNKNOWN_PTYPE);
1267 }
1268
1269 static const struct part_type_desc *
mbr_get_gen_type_desc(unsigned int pt)1270 mbr_get_gen_type_desc(unsigned int pt)
1271 {
1272
1273 if (known_part_types == 0)
1274 map_mbr_part_types();
1275
1276 if (pt >= __arraycount(mbr_gen_type_desc))
1277 return NULL;
1278
1279 if (mbr_gen_type_desc[pt].gen.short_desc != NULL)
1280 return &mbr_gen_type_desc[pt].gen;
1281
1282 return mbr_new_custom_part_type(pt);
1283 }
1284
1285 static const struct part_type_desc *
mbr_get_generic_part_type(enum part_type pt)1286 mbr_get_generic_part_type(enum part_type pt)
1287 {
1288 switch (pt) {
1289 case PT_root:
1290 return mbr_get_gen_type_desc(MBR_PTYPE_NETBSD);
1291 case PT_FAT:
1292 return mbr_get_gen_type_desc(MBR_PTYPE_FAT32L);
1293 case PT_EXT2:
1294 return mbr_get_gen_type_desc(MBR_PTYPE_LNXEXT2);
1295 case PT_EFI_SYSTEM:
1296 return mbr_get_gen_type_desc(MBR_PTYPE_EFI);
1297 default:
1298 break;
1299 }
1300 assert(false);
1301 return NULL;
1302 }
1303
1304 static void
mbr_partition_to_info(const struct mbr_partition * mp,daddr_t start_off,struct disk_part_info * info)1305 mbr_partition_to_info(const struct mbr_partition *mp, daddr_t start_off,
1306 struct disk_part_info *info)
1307 {
1308 memset(info, 0, sizeof(*info));
1309 info->start = mp->mbrp_start + start_off;
1310 info->size = mp->mbrp_size;
1311 info->nat_type = mbr_get_gen_type_desc(mp->mbrp_type);
1312 if (mp->mbrp_type == MBR_PTYPE_NETBSD) {
1313 info->flags |= PTI_SEC_CONTAINER;
1314 } else if (MBR_IS_EXTENDED(mp->mbrp_type))
1315 info->flags |= PTI_PSCHEME_INTERNAL;
1316 }
1317
1318 static bool
mbr_part_apply(const struct disk_partitions * arg,part_id id,bool (* func)(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void *),void * cookie)1319 mbr_part_apply(const struct disk_partitions *arg, part_id id,
1320 bool (*func)(const struct disk_partitions *arg, part_id id,
1321 const mbr_info_t *mb, int i, bool primary,
1322 const struct mbr_partition *mp, void *),
1323 void *cookie)
1324 {
1325 const struct mbr_disk_partitions *parts =
1326 (const struct mbr_disk_partitions*)arg;
1327 part_id i, j, no;
1328 const mbr_info_t *m = &parts->mbr, *me;
1329
1330 no = 0;
1331 for (i = 0; i < MBR_PART_COUNT; i++) {
1332 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1333 continue;
1334
1335 if (no == id) {
1336 return func(arg, id, m, i, true,
1337 &m->mbr.mbr_parts[i], cookie);
1338 }
1339 no++;
1340
1341 if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
1342 for (me = m->extended; me != NULL; me = me->extended) {
1343 for (j = 0; j < MBR_PART_COUNT; j++) {
1344 if (me->mbr.mbr_parts[j].mbrp_type ==
1345 MBR_PTYPE_UNUSED)
1346 continue;
1347 if (j > 0 && MBR_IS_EXTENDED(
1348 me->mbr.mbr_parts[j].mbrp_type))
1349 break;
1350 if (no == id) {
1351 return func(arg, id, me, j,
1352 false,
1353 &me->mbr.mbr_parts[j],
1354 cookie);
1355 }
1356 no++;
1357 }
1358 }
1359 }
1360 }
1361
1362
1363 return false;
1364 }
1365
1366 static bool
mbr_do_get_part_info(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)1367 mbr_do_get_part_info(const struct disk_partitions *arg, part_id id,
1368 const mbr_info_t *mb, int i, bool primary,
1369 const struct mbr_partition *mp, void *cookie)
1370 {
1371 struct disk_part_info *info = cookie;
1372 const struct mbr_disk_partitions *parts =
1373 (const struct mbr_disk_partitions*)arg;
1374
1375 mbr_partition_to_info(mp, mb->sector, info);
1376 if (mp->mbrp_start + mb->sector == parts->target)
1377 info->flags |= PTI_INSTALL_TARGET;
1378 if (mb->last_mounted[i] != NULL && mb->last_mounted[i][0] != 0)
1379 info->last_mounted = mb->last_mounted[i];
1380 if (mb->fs_type[i] != FS_UNUSED) {
1381 info->fs_type = mb->fs_type[i];
1382 info->fs_sub_type = mb->fs_sub_type[i];
1383 } else {
1384 info->fs_sub_type = 0;
1385 switch (mp->mbrp_type) {
1386 case MBR_PTYPE_FAT12:
1387 case MBR_PTYPE_FAT16S:
1388 case MBR_PTYPE_FAT16B:
1389 case MBR_PTYPE_FAT32:
1390 case MBR_PTYPE_FAT32L:
1391 case MBR_PTYPE_FAT16L:
1392 case MBR_PTYPE_OS2_DOS12:
1393 case MBR_PTYPE_OS2_DOS16S:
1394 case MBR_PTYPE_OS2_DOS16B:
1395 case MBR_PTYPE_HID_FAT32:
1396 case MBR_PTYPE_HID_FAT32_LBA:
1397 case MBR_PTYPE_HID_FAT16_LBA:
1398 case MBR_PTYPE_MDOS_FAT12:
1399 case MBR_PTYPE_MDOS_FAT16S:
1400 case MBR_PTYPE_MDOS_EXT:
1401 case MBR_PTYPE_MDOS_FAT16B:
1402 case MBR_PTYPE_SPEEDSTOR_16S:
1403 case MBR_PTYPE_EFI:
1404 info->fs_type = FS_MSDOS;
1405 info->fs_sub_type = mp->mbrp_type;
1406 break;
1407 case MBR_PTYPE_LNXEXT2:
1408 info->fs_type = FS_EX2FS;
1409 break;
1410 case MBR_PTYPE_XENIX_ROOT:
1411 case MBR_PTYPE_XENIX_USR:
1412 info->fs_type = FS_SYSV;
1413 break;
1414 case MBR_PTYPE_NTFS:
1415 info->fs_type = FS_NTFS;
1416 break;
1417 case MBR_PTYPE_APPLE_HFS:
1418 info->fs_type = FS_HFS;
1419 break;
1420 case MBR_PTYPE_VMWARE:
1421 info->fs_type = FS_VMFS;
1422 break;
1423 case MBR_PTYPE_AST_SWAP:
1424 case MBR_PTYPE_DRDOS_LSWAP:
1425 case MBR_PTYPE_LNXSWAP:
1426 case MBR_PTYPE_BSDI_SWAP:
1427 case MBR_PTYPE_HID_LNX_SWAP:
1428 case MBR_PTYPE_VMWARE_SWAP:
1429 info->fs_type = FS_SWAP;
1430 break;
1431 }
1432 }
1433 return true;
1434 }
1435
1436 static bool
get_wedge_devname(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)1437 get_wedge_devname(const struct disk_partitions *arg, part_id id,
1438 const mbr_info_t *mb, int i, bool primary,
1439 const struct mbr_partition *mp, void *cookie)
1440 {
1441 char **res = cookie;
1442
1443 if (!res)
1444 return false;
1445
1446 *res = __UNCONST(mb->wedge[i]);
1447 return true;
1448 }
1449
1450 static bool
mbr_part_get_wedge(const struct disk_partitions * arg,part_id id,char ** res)1451 mbr_part_get_wedge(const struct disk_partitions *arg, part_id id,
1452 char **res)
1453 {
1454 return mbr_part_apply(arg, id, get_wedge_devname, res);
1455 }
1456
1457 static bool
mbr_get_part_info(const struct disk_partitions * arg,part_id id,struct disk_part_info * info)1458 mbr_get_part_info(const struct disk_partitions *arg, part_id id,
1459 struct disk_part_info *info)
1460 {
1461 return mbr_part_apply(arg, id, mbr_do_get_part_info, info);
1462 }
1463
1464 static bool
type_can_change(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)1465 type_can_change(const struct disk_partitions *arg, part_id id,
1466 const mbr_info_t *mb, int i, bool primary,
1467 const struct mbr_partition *mp, void *cookie)
1468 {
1469 /*
1470 * The extended partition can only change type or be
1471 * deleted if it is empty
1472 */
1473 if (!MBR_IS_EXTENDED(mp->mbrp_type))
1474 return true;
1475 return primary && mb->extended == NULL;
1476 }
1477
1478 static bool
mbr_part_type_can_change(const struct disk_partitions * arg,part_id id)1479 mbr_part_type_can_change(const struct disk_partitions *arg, part_id id)
1480 {
1481 return mbr_part_apply(arg, id, type_can_change, NULL);
1482 }
1483
1484 struct part_get_str_data {
1485 char *str;
1486 size_t avail_space;
1487 size_t col;
1488 };
1489
1490
1491 static bool
mbr_get_part_table_str(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)1492 mbr_get_part_table_str(const struct disk_partitions *arg, part_id id,
1493 const mbr_info_t *mb, int i, bool primary,
1494 const struct mbr_partition *mp, void *cookie)
1495 {
1496 struct part_get_str_data *data = cookie;
1497 char *str = data->str;
1498 const struct part_type_desc *ptype;
1499
1500 switch (data->col) {
1501 case 0:
1502 ptype = mbr_get_gen_type_desc(mp->mbrp_type);
1503 if (ptype != NULL)
1504 strncpy(str, ptype->description, data->avail_space);
1505 else
1506 snprintf(str, data->avail_space, "%u", mp->mbrp_type);
1507 str[data->avail_space-1] = 0;
1508 break;
1509 case 1:
1510 if (mb->last_mounted[i])
1511 strlcpy(str, mb->last_mounted[i], data->avail_space);
1512 else
1513 *str = 0;
1514 break;
1515 #ifdef BOOTSEL
1516 case 2:
1517 if (mb->mbrb.mbrbs_nametab[i][0] != 0)
1518 strlcpy(str, mb->mbrb.mbrbs_nametab[i],
1519 data->avail_space);
1520 else
1521 *str = 0;
1522 break;
1523 #endif
1524 }
1525
1526 return true;
1527 }
1528
1529 static bool
mbr_table_str(const struct disk_partitions * arg,part_id id,size_t col,char * str,size_t avail_space)1530 mbr_table_str(const struct disk_partitions *arg, part_id id, size_t col,
1531 char *str, size_t avail_space)
1532 {
1533 struct part_get_str_data data;
1534
1535 data.str = str;
1536 data.avail_space = avail_space;
1537 data.col = col;
1538 return mbr_part_apply(arg, id, mbr_get_part_table_str, &data);
1539 }
1540
1541 static bool
mbr_get_part_attr_str(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)1542 mbr_get_part_attr_str(const struct disk_partitions *arg, part_id id,
1543 const mbr_info_t *mb, int i, bool primary,
1544 const struct mbr_partition *mp, void *cookie)
1545 {
1546 #ifdef BOOTSEL
1547 const struct mbr_disk_partitions *parts =
1548 (const struct mbr_disk_partitions*)arg;
1549 #endif
1550 struct part_get_str_data *data = cookie;
1551 static const char *flags = NULL;
1552 char *str = data->str;
1553
1554 if (flags == NULL)
1555 flags = msg_string(MSG_mbr_flags);
1556
1557 if (mp->mbrp_flag & MBR_PFLAG_ACTIVE)
1558 *str++ = flags[0];
1559 #ifdef BOOTSEL
1560 if (parts->mbr.bootsec == mb->sector+mp->mbrp_start)
1561 *str++ = flags[1];
1562 #endif
1563 *str = 0;
1564 return true;
1565 }
1566
1567 static bool
mbr_part_attr_str(const struct disk_partitions * arg,part_id id,char * str,size_t avail_space)1568 mbr_part_attr_str(const struct disk_partitions *arg, part_id id,
1569 char *str, size_t avail_space)
1570 {
1571 struct part_get_str_data data;
1572
1573 if (avail_space < 3)
1574 return false;
1575
1576 data.str = str;
1577 data.avail_space = avail_space;
1578 return mbr_part_apply(arg, id, mbr_get_part_attr_str, &data);
1579 }
1580
1581 static bool
mbr_info_to_partitition(const struct disk_part_info * info,struct mbr_partition * mp,uint sector,struct mbr_info_t * mb,size_t index,const char ** err_msg)1582 mbr_info_to_partitition(const struct disk_part_info *info,
1583 struct mbr_partition *mp, uint sector,
1584 struct mbr_info_t *mb, size_t index, const char **err_msg)
1585 {
1586 size_t pt = mbr_type_from_gen_desc(info->nat_type);
1587 if (info->start + info->size > UINT_MAX
1588 || pt > __arraycount(mbr_gen_type_desc)) {
1589 if (err_msg)
1590 *err_msg = err_outofmem;
1591 return false;
1592 }
1593 mp->mbrp_start = info->start - sector;
1594 mp->mbrp_size = info->size;
1595 mp->mbrp_type = pt;
1596 if (info->flags & PTI_INSTALL_TARGET) {
1597 mp->mbrp_flag |= MBR_PFLAG_ACTIVE;
1598 #ifdef BOOTSEL
1599 strcpy(mb->mbrb.mbrbs_nametab[index], "NetBSD");
1600 #endif
1601 }
1602
1603 return true;
1604 }
1605
1606 static bool
inside_ext_part(mbr_info_t * m,daddr_t start)1607 inside_ext_part(mbr_info_t *m, daddr_t start)
1608 {
1609 size_t i;
1610 struct mbr_partition *mp = NULL;
1611
1612 for (i = 0; i < MBR_PART_COUNT; i++) {
1613 if (!MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1614 continue;
1615 mp = &m->mbr.mbr_parts[i];
1616 break;
1617 }
1618
1619 if (mp == NULL) {
1620 assert(false);
1621 return false;
1622 }
1623
1624 if (mp->mbrp_start > start)
1625 return false;
1626
1627 return true;
1628 }
1629
1630 static void
adjust_ext_part(mbr_info_t * m,daddr_t start,daddr_t size)1631 adjust_ext_part(mbr_info_t *m, daddr_t start, daddr_t size)
1632 {
1633 size_t i;
1634 struct mbr_partition *mp = NULL;
1635
1636 for (i = 0; i < MBR_PART_COUNT; i++) {
1637 if (!MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1638 continue;
1639 mp = &m->mbr.mbr_parts[i];
1640 break;
1641 }
1642
1643 if (mp == NULL) {
1644 assert(false);
1645 return;
1646 }
1647
1648 if (mp->mbrp_start + mp->mbrp_size >= start + size)
1649 return;
1650
1651 daddr_t new_end = start + size;
1652 mp->mbrp_size = new_end - mp->mbrp_start;
1653 }
1654
1655 static bool
ext_part_good(mbr_info_t * m,daddr_t ext_start,daddr_t ext_size)1656 ext_part_good(mbr_info_t *m, daddr_t ext_start, daddr_t ext_size)
1657 {
1658 for (m = m->extended; m != NULL; m = m->extended) {
1659 for (size_t i = 0; i < MBR_PART_COUNT; i++) {
1660 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1661 continue;
1662
1663 if (i > 0 &&
1664 MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1665 break;
1666
1667 daddr_t pstart = m->mbr.mbr_parts[i].mbrp_start +
1668 m->sector;
1669 daddr_t pend = pstart + m->mbr.mbr_parts[i].mbrp_size;
1670
1671 if (pstart < ext_start || pend > ext_start+ext_size)
1672 return false;
1673 }
1674 }
1675
1676 return true;
1677 }
1678
1679 static bool
mbr_set_part_info(struct disk_partitions * arg,part_id id,const struct disk_part_info * info,const char ** err_msg)1680 mbr_set_part_info(struct disk_partitions *arg, part_id id,
1681 const struct disk_part_info *info, const char **err_msg)
1682 {
1683 struct mbr_disk_partitions *parts =
1684 (struct mbr_disk_partitions*)arg;
1685 struct disk_part_info data = *info;
1686 part_id i, j, no, ext_ndx, t;
1687 mbr_info_t *m = &parts->mbr, *me;
1688 uint pt = mbr_type_from_gen_desc(info->nat_type);
1689
1690 if (MBR_IS_EXTENDED(pt)) {
1691 /* check for duplicate ext part */
1692 no = 0;
1693 t = ext_ndx = MBR_PART_COUNT;
1694 for (i = 0; i < MBR_PART_COUNT; i++) {
1695 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1696 continue;
1697 if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1698 ext_ndx = i;
1699 if (no == id)
1700 t = i;
1701 no++;
1702 }
1703 if (ext_ndx < MBR_PART_COUNT && t != ext_ndx) {
1704 if (err_msg)
1705 *err_msg =
1706 msg_string(MSG_Only_one_extended_ptn);
1707 return false;
1708 }
1709 /* this partition becomes an extended one, apply alignment */
1710 data.start = max(roundup(data.start, parts->ext_ptn_alignment),
1711 parts->ext_ptn_alignment);
1712 }
1713
1714 no = 0;
1715 for (i = 0; i < MBR_PART_COUNT; i++) {
1716 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1717 continue;
1718
1719 if (no == id)
1720 goto found;
1721 no++;
1722
1723 if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
1724 for (me = m->extended; me != NULL; me = me->extended) {
1725 for (j = 0; j < MBR_PART_COUNT; j++) {
1726 if (me->mbr.mbr_parts[j].mbrp_type ==
1727 MBR_PTYPE_UNUSED)
1728 continue;
1729 if (j > 0 && MBR_IS_EXTENDED(
1730 me->mbr.mbr_parts[j].mbrp_type))
1731 break;
1732 if (no == id) {
1733 i = j;
1734 m = me;
1735 goto found;
1736 }
1737 no++;
1738 }
1739 }
1740 }
1741 }
1742
1743 if (err_msg)
1744 *err_msg = INTERNAL_ERROR;
1745 return false;
1746
1747 found:
1748 /*
1749 * We assume that m is the mbr we want to update and
1750 * i is the local partition index into it.
1751 */
1752 if (m == &parts->mbr) {
1753 if (MBR_IS_EXTENDED(
1754 m->mbr.mbr_parts[i].mbrp_type) &&
1755 !ext_part_good(&parts->mbr, data.start, data.size)) {
1756 if (err_msg)
1757 *err_msg =
1758 MSG_mbr_ext_nofit;
1759 return false;
1760 }
1761 } else if (!inside_ext_part(&parts->mbr, data.start)) {
1762 if (err_msg)
1763 *err_msg = msg_string(MSG_mbr_inside_ext);
1764 return false;
1765 }
1766 uint start = data.start, size = data.size;
1767 uint oldstart = m->mbr.mbr_parts[i].mbrp_start + m->sector;
1768 if (parts->ptn_0_offset > 0 &&
1769 start < parts->ptn_0_offset)
1770 start = parts->ptn_0_offset;
1771 if (find_mbr_space(m, &start, &size, start, parts->dp.disk_size,
1772 oldstart, false) < 0) {
1773 if (err_msg != NULL)
1774 *err_msg = INTERNAL_ERROR;
1775 return false;
1776 }
1777 data.start = start;
1778 if (size < data.size)
1779 data.size = size;
1780 uint old_start = m->mbr.mbr_parts[i].mbrp_start;
1781 if (!mbr_info_to_partitition(&data,
1782 &m->mbr.mbr_parts[i], m->sector, m, i, err_msg))
1783 return false;
1784 if (data.flags & PTI_INSTALL_TARGET)
1785 parts->target = start;
1786 else if (old_start == parts->target)
1787 parts->target = -1;
1788 if (data.last_mounted && m->last_mounted[i] &&
1789 data.last_mounted != m->last_mounted[i]) {
1790 free(__UNCONST(m->last_mounted[i]));
1791 m->last_mounted[i] = strdup(data.last_mounted);
1792 }
1793 if (data.fs_type != 0)
1794 m->fs_type[i] = data.fs_type;
1795 if (data.fs_sub_type != 0)
1796 m->fs_sub_type[i] = data.fs_sub_type;
1797
1798 if (m == &parts->mbr) {
1799 if (m->mbr.mbr_parts[i].mbrp_start !=
1800 old_start)
1801 mbr_sort_main_mbr(&m->mbr);
1802 } else {
1803 adjust_ext_part(&parts->mbr,
1804 data.start, data.size);
1805 }
1806 mbr_calc_free_space(parts);
1807 return true;
1808 }
1809
1810 static bool
mbr_find_netbsd(const struct mbr_info_t * m,uint start,struct disk_part_info * info)1811 mbr_find_netbsd(const struct mbr_info_t *m, uint start,
1812 struct disk_part_info *info)
1813 {
1814 size_t i;
1815 bool prim = true;
1816
1817 do {
1818 for (i = 0; i < MBR_PART_COUNT; i++) {
1819 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
1820 continue;
1821
1822 if (!prim && i > 0 &&
1823 MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
1824 break;
1825
1826 const struct mbr_partition *mp = &m->mbr.mbr_parts[i];
1827 if (mp->mbrp_type != MBR_PTYPE_NETBSD)
1828 continue;
1829
1830 mbr_partition_to_info(mp, m->sector, info);
1831 if (m->last_mounted[i] && *m->last_mounted[i] != 0)
1832 info->last_mounted =
1833 m->last_mounted[i];
1834 info->fs_type = m->fs_type[i];
1835 info->fs_sub_type = m->fs_sub_type[i];
1836 if (start > 0 && start != info->start)
1837 continue;
1838 return true;
1839 }
1840 prim = false;
1841 } while ((m = m->extended));
1842
1843 return false;
1844 }
1845
1846 static struct disk_partitions *
mbr_read_disklabel(struct disk_partitions * arg,daddr_t start,bool force_empty)1847 mbr_read_disklabel(struct disk_partitions *arg, daddr_t start, bool force_empty)
1848 {
1849 struct mbr_disk_partitions *myparts =
1850 (struct mbr_disk_partitions*)arg;
1851 struct disk_part_info part;
1852 struct disk_part_free_space space;
1853
1854 if (force_empty && myparts->dlabel)
1855 myparts->dlabel->pscheme->delete_all_partitions(
1856 myparts->dlabel);
1857
1858 if (myparts->dlabel == NULL) {
1859 /*
1860 * Find the NetBSD MBR partition
1861 */
1862 if (!mbr_find_netbsd(&myparts->mbr, start, &part)) {
1863 if (!force_empty)
1864 return NULL;
1865
1866 /* add a "whole disk" NetBSD partition */
1867 memset(&part, 0, sizeof part);
1868 part.start = min(myparts->ptn_0_offset,start);
1869 if (!mbr_get_free_spaces(arg, &space, 1,
1870 part.start, myparts->ptn_alignment, -1, -1))
1871 return NULL;
1872 part.start = space.start;
1873 part.size = space.size;
1874 part.nat_type = &mbr_gen_type_desc[MBR_PTYPE_NETBSD].gen;
1875 mbr_add_part(arg, &part, NULL);
1876 if (!mbr_find_netbsd(&myparts->mbr, start, &part))
1877 return NULL;
1878 }
1879
1880 if (!force_empty) {
1881 myparts->dlabel = disklabel_parts.read_from_disk(
1882 myparts->dp.disk, part.start, part.size,
1883 myparts->dp.bytes_per_sector, &disklabel_parts);
1884 if (myparts->dlabel != NULL)
1885 myparts->dlabel->parent = &myparts->dp;
1886 }
1887
1888 if (myparts->dlabel == NULL && part.size > 0) {
1889 /* we just created the outer partitions? */
1890 myparts->dlabel =
1891 disklabel_parts.create_new_for_disk(
1892 myparts->dp.disk, part.start, part.size,
1893 false, &myparts->dp);
1894 }
1895
1896 if (myparts->dlabel != NULL)
1897 myparts->dlabel->pscheme->change_disk_geom(
1898 myparts->dlabel, myparts->geo_cyl,
1899 myparts->geo_head, myparts->geo_sec);
1900 }
1901 return myparts->dlabel;
1902 }
1903
1904 static int
get_mapping(struct mbr_partition * parts,int i,int * cylinder,int * head,int * sector,daddr_t * absolute)1905 get_mapping(struct mbr_partition *parts, int i,
1906 int *cylinder, int *head, int *sector, daddr_t *absolute)
1907 {
1908 struct mbr_partition *apart = &parts[i / 2];
1909
1910 if (apart->mbrp_type == MBR_PTYPE_UNUSED)
1911 return -1;
1912 if (i % 2 == 0) {
1913 *cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
1914 *head = apart->mbrp_shd;
1915 *sector = MBR_PSECT(apart->mbrp_ssect) - 1;
1916 *absolute = le32toh(apart->mbrp_start);
1917 } else {
1918 *cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
1919 *head = apart->mbrp_ehd;
1920 *sector = MBR_PSECT(apart->mbrp_esect) - 1;
1921 *absolute = le32toh(apart->mbrp_start)
1922 + le32toh(apart->mbrp_size) - 1;
1923 }
1924 /* Sanity check the data against max values */
1925 if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
1926 /* cannot be a CHS mapping */
1927 return -1;
1928
1929 return 0;
1930 }
1931
1932 static bool
mbr_delete_all(struct disk_partitions * arg)1933 mbr_delete_all(struct disk_partitions *arg)
1934 {
1935 struct mbr_disk_partitions *myparts = (struct mbr_disk_partitions*)arg;
1936 struct mbr_sector *mbrs = &myparts->mbr.mbr;
1937 struct mbr_info_t *mbri = &myparts->mbr;
1938 mbr_info_t *ext;
1939 struct mbr_partition *part;
1940
1941 part = &mbrs->mbr_parts[0];
1942 /* Set the partition information for full disk usage. */
1943 while ((ext = mbri->extended)) {
1944 mbri->extended = ext->extended;
1945 free_mbr_info(ext);
1946 }
1947 memset(part, 0, MBR_PART_COUNT * sizeof *part);
1948 #ifdef BOOTSEL
1949 memset(&mbri->mbrb, 0, sizeof mbri->mbrb);
1950 #endif
1951
1952 /*
1953 * We may have changed alignment settings due to partitions
1954 * ending on an MB boundary - undo that, now that the partitions
1955 * are gone.
1956 */
1957 mbr_change_disk_geom(arg, myparts->geo_cyl, myparts->geo_head,
1958 myparts->geo_sec);
1959
1960 return true;
1961 }
1962
1963 /*
1964 * helper function to fix up mbrp_start and mbrp_size for the
1965 * extended MBRs "partition b" entries after addition/deletion
1966 * of some partition.
1967 */
1968 static void
mbr_fixup_ext_chain(mbr_info_t * primary,uint ext_start,uint ext_end)1969 mbr_fixup_ext_chain(mbr_info_t *primary, uint ext_start, uint ext_end)
1970 {
1971 for (mbr_info_t *m = primary->extended; m != NULL; m = m->extended) {
1972 if (m->extended == NULL) {
1973 m->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_UNUSED;
1974 m->mbr.mbr_parts[1].mbrp_start = 0;
1975 m->mbr.mbr_parts[1].mbrp_size = 0;
1976 } else {
1977 uint n_end, n_start = m->extended->sector;
1978 if (m->extended->extended)
1979 n_end = m->extended->extended->sector;
1980 else
1981 n_end = ext_end;
1982 m->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
1983 m->mbr.mbr_parts[1].mbrp_start = n_start - ext_start;
1984 m->mbr.mbr_parts[1].mbrp_size = n_end - n_start;
1985 }
1986 }
1987 }
1988
1989 struct delete_part_args {
1990 struct mbr_disk_partitions *parts;
1991 daddr_t start, size;
1992 const char **err_msg;
1993 };
1994
1995 static bool
mbr_do_delete_part(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)1996 mbr_do_delete_part(const struct disk_partitions *arg, part_id id,
1997 const mbr_info_t *mb, int i, bool primary,
1998 const struct mbr_partition *mp, void *cookie)
1999 {
2000 struct delete_part_args *marg = cookie;
2001 bool is_ext_part = MBR_IS_EXTENDED(mp->mbrp_type);
2002
2003 /* can not delete non-empty extended partitions */
2004 if (MBR_IS_EXTENDED(mp->mbrp_type)
2005 && marg->parts->mbr.extended != NULL) {
2006 if (marg->err_msg)
2007 *marg->err_msg = msg_string(MSG_mbr_ext_not_empty);
2008 return false;
2009 }
2010
2011 /* return position/size to caller */
2012 marg->start = mb->sector + mp->mbrp_start;
2013 marg->size = mp->mbrp_size;
2014
2015 if (primary) {
2016 /* if deleting the primary extended partition, just kill it */
2017 struct mbr_partition *md = &marg->parts->mbr.mbr.mbr_parts[i];
2018 md->mbrp_size = 0;
2019 md->mbrp_start = 0;
2020 md->mbrp_type = MBR_PTYPE_UNUSED;
2021 if (marg->parts->mbr.last_mounted[i]) {
2022 free(__UNCONST(marg->parts->mbr.last_mounted[i]));
2023 marg->parts->mbr.last_mounted[i] = NULL;
2024 }
2025 if (is_ext_part) {
2026 for (mbr_info_t *m = marg->parts->mbr.extended;
2027 m != NULL; ) {
2028 mbr_info_t *n = m->extended;
2029 free_mbr_info(m);
2030 m = n;
2031 }
2032 marg->parts->mbr.extended = NULL;
2033 }
2034 } else {
2035 /* find the size of the primary extended partition */
2036 uint ext_start = 0, ext_size = 0;
2037 for (i = 0; i < MBR_PART_COUNT; i++) {
2038 if (!MBR_IS_EXTENDED(marg->parts->mbr.mbr.mbr_parts[i]
2039 .mbrp_type))
2040 continue;
2041 ext_start = marg->parts->mbr.mbr.mbr_parts[i]
2042 .mbrp_start;
2043 ext_size = marg->parts->mbr.mbr.mbr_parts[i]
2044 .mbrp_size;
2045 break;
2046 }
2047
2048 /*
2049 * If we are in an extended partition chain, unlink this MBR,
2050 * unless it is the very first one at the start of the extended
2051 * partition (we would have no previous ext mbr to fix up
2052 * the chain in that case)
2053 */
2054 if (marg->parts->mbr.extended == mb) {
2055 struct mbr_partition *part =
2056 &marg->parts->mbr.extended->mbr.mbr_parts[0];
2057 part->mbrp_type = MBR_PTYPE_UNUSED;
2058 part->mbrp_start = 0;
2059 part->mbrp_size = 0;
2060 } else {
2061 mbr_info_t *p, *last;
2062 for (last = NULL, p = &marg->parts->mbr; p != NULL;
2063 last = p, p = p->extended)
2064 if (p == mb)
2065 break;
2066 if (last == NULL) {
2067 if (marg->err_msg != NULL)
2068 *marg->err_msg= INTERNAL_ERROR;
2069 return false;
2070 }
2071 last->extended = p->extended;
2072 free_mbr_info(p);
2073 if (last == &marg->parts->mbr && last->extended &&
2074 last->extended->extended == NULL &&
2075 last->extended->mbr.mbr_parts[0].mbrp_type ==
2076 MBR_PTYPE_UNUSED) {
2077 /*
2078 * we deleted the last extended sector,
2079 * remove the whole chain
2080 */
2081 free_mbr_info(last->extended);
2082 last->extended = NULL;
2083 }
2084 }
2085 mbr_fixup_ext_chain(&marg->parts->mbr, ext_start,
2086 ext_start+ext_size);
2087 }
2088 mbr_calc_free_space(marg->parts);
2089 return true;
2090 }
2091
2092 static bool
mbr_delete_part(struct disk_partitions * arg,part_id pno,const char ** err_msg)2093 mbr_delete_part(struct disk_partitions *arg, part_id pno, const char **err_msg)
2094 {
2095 struct mbr_disk_partitions *parts =
2096 (struct mbr_disk_partitions*)arg;
2097 struct delete_part_args data = { .parts = parts, .err_msg = err_msg };
2098
2099 if (!mbr_part_apply(arg, pno, mbr_do_delete_part, &data)) {
2100 if (err_msg)
2101 *err_msg = INTERNAL_ERROR;
2102 return false;
2103 }
2104
2105 if (parts->target == data.start)
2106 parts->target = ~0U;
2107
2108 if (parts->dlabel) {
2109 /*
2110 * If we change the mbr partitioning, the we must
2111 * remove any references in the netbsd disklabel
2112 * to the part we changed.
2113 */
2114 parts->dlabel->pscheme->delete_partitions_in_range(
2115 parts->dlabel, data.start, data.size);
2116 }
2117
2118 if (err_msg)
2119 *err_msg = NULL;
2120
2121 dump_mbr(&parts->mbr, "after delete");
2122 return true;
2123 }
2124
2125 static struct mbr_partition *
mbr_ptr_from_start(mbr_info_t * m,daddr_t start)2126 mbr_ptr_from_start(mbr_info_t *m, daddr_t start)
2127 {
2128 bool primary = true;
2129
2130 do {
2131 for (uint i = 0; i < MBR_PART_COUNT; i++) {
2132 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2133 continue;
2134 if (!primary &&
2135 MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
2136 break;
2137
2138 daddr_t pstart = m->sector +
2139 m->mbr.mbr_parts[i].mbrp_start;
2140 if (pstart == start)
2141 return &m->mbr.mbr_parts[i];
2142
2143 }
2144 primary = false;
2145 } while ((m = m->extended));
2146
2147 return NULL;
2148 }
2149
2150 static uint8_t
mbr_type_from_start(const mbr_info_t * m,daddr_t start)2151 mbr_type_from_start(const mbr_info_t *m, daddr_t start)
2152 {
2153 bool primary = true;
2154
2155 do {
2156 for (uint i = 0; i < MBR_PART_COUNT; i++) {
2157 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2158 continue;
2159 if (!primary &&
2160 MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
2161 break;
2162
2163 daddr_t pstart = m->sector +
2164 m->mbr.mbr_parts[i].mbrp_start;
2165 if (pstart == start)
2166 return m->mbr.mbr_parts[i].mbrp_type;
2167
2168 }
2169 primary = false;
2170 } while ((m = m->extended));
2171
2172 return MBR_PTYPE_UNUSED;
2173 }
2174
2175 static part_id
mbr_add_part(struct disk_partitions * arg,const struct disk_part_info * info,const char ** errmsg)2176 mbr_add_part(struct disk_partitions *arg,
2177 const struct disk_part_info *info, const char **errmsg)
2178 {
2179 struct mbr_disk_partitions *parts =
2180 (struct mbr_disk_partitions*)arg;
2181 part_id i, j, no, free_primary = UINT_MAX;
2182 mbr_info_t *m = &parts->mbr, *me, *last, *t;
2183 daddr_t ext_start = 0, ext_size = 0;
2184 uint start, size;
2185 struct disk_part_info data = *info;
2186 struct mbr_partition *newp;
2187
2188 if (errmsg != NULL)
2189 *errmsg = NULL;
2190
2191 assert(info->nat_type != NULL);
2192 if (info->nat_type == NULL) {
2193 if (errmsg != NULL)
2194 *errmsg = INTERNAL_ERROR;
2195 return NO_PART;
2196 }
2197 if (mbr_type_from_gen_desc(info->nat_type) == MBR_PTYPE_UNUSED) {
2198 if (errmsg != NULL)
2199 *errmsg = INTERNAL_ERROR;
2200 return NO_PART;
2201 }
2202
2203 /* do we have free primary slots and/or an extended partition? */
2204 for (i = 0; i < MBR_PART_COUNT; i++) {
2205 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED
2206 && free_primary > MBR_PART_COUNT)
2207 free_primary = i;
2208 if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
2209 ext_start = m->mbr.mbr_parts[i].mbrp_start+m->sector;
2210 ext_size = m->mbr.mbr_parts[i].mbrp_size;
2211 continue;
2212 }
2213 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED
2214 && m->mbr.mbr_parts[i].mbrp_size == 0)
2215 continue;
2216 }
2217 if (ext_start > 0 && ext_size > 0 &&
2218 MBR_IS_EXTENDED(mbr_type_from_gen_desc(info->nat_type))) {
2219 /*
2220 * Do not allow a second extended partition
2221 */
2222 if (errmsg)
2223 *errmsg = MSG_Only_one_extended_ptn;
2224 return NO_PART;
2225 }
2226
2227 /* should this go into the extended partition? */
2228 if (ext_size > 0 && info->start >= ext_start
2229 && info->start < ext_start + ext_size) {
2230
2231 /* must fit into the extended partition */
2232 if (info->start + info->size > ext_start + ext_size) {
2233 if (errmsg != NULL)
2234 *errmsg = MSG_mbr_ext_nofit;
2235 return NO_PART;
2236 }
2237
2238 /* walk the chain untill we find a proper insert position */
2239 daddr_t e_end, e_start;
2240 for (last = m, m = m->extended; m != NULL;
2241 last = m, m = m->extended) {
2242 e_start = m->mbr.mbr_parts[1].mbrp_start
2243 + ext_start;
2244 e_end = e_start + m->mbr.mbr_parts[1].mbrp_size;
2245 if (data.start <= e_start)
2246 break;
2247 }
2248 if (m == NULL) {
2249 /* add new tail record */
2250 e_end = ext_start + ext_size;
2251 /* new part needs to fit inside primary extended one */
2252 if (data.start + data.size > e_end) {
2253 if (errmsg)
2254 *errmsg = MSG_No_free_space;
2255 return NO_PART;
2256 }
2257 } else if (data.start + data.size > e_start) {
2258 /* new part needs to fit before next extended */
2259 if (errmsg)
2260 *errmsg = MSG_No_free_space;
2261 return NO_PART;
2262 }
2263 /*
2264 * now last points to previous mbr (maybe primary), m
2265 * points to the one that should take the new partition
2266 * or we have to insert a new mbr between the two, or
2267 * m needs to be split and we go into the one after it.
2268 */
2269 if (m && m->mbr.mbr_parts[0].mbrp_type == MBR_PTYPE_UNUSED) {
2270 /* empty slot, we can just use it */
2271 newp = &m->mbr.mbr_parts[0];
2272 mbr_info_to_partitition(&data, &m->mbr.mbr_parts[0],
2273 m->sector, m, 0, errmsg);
2274 if (data.last_mounted && m->last_mounted[0] &&
2275 data.last_mounted != m->last_mounted[0]) {
2276 free(__UNCONST(m->last_mounted[0]));
2277 m->last_mounted[0] = strdup(data.last_mounted);
2278 }
2279 } else {
2280 mbr_info_t *new_mbr;
2281 if (m == NULL)
2282 m = last;
2283 daddr_t p_start = m->mbr.mbr_parts[0].mbrp_start
2284 + m->sector;
2285 daddr_t p_end = p_start
2286 + m->mbr.mbr_parts[0].mbrp_size;
2287 bool before;
2288 if (m == last || data.start > p_end)
2289 before = false;
2290 else if (data.start + data.size < p_start)
2291 before = true;
2292 else {
2293 if (errmsg)
2294 *errmsg = MSG_No_free_space;
2295 return NO_PART;
2296 }
2297 new_mbr = calloc(1, sizeof *new_mbr);
2298 if (!new_mbr) {
2299 if (errmsg)
2300 *errmsg = err_outofmem;
2301 return NO_PART;
2302 }
2303 new_mbr->mbr.mbr_magic = htole16(MBR_MAGIC);
2304 new_mbr->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
2305 if (before) {
2306 /*
2307 * This is a hypthetical case where
2308 * an extended MBR uses an unusual high
2309 * offset (m->sector to parts[0].mbrp_start)
2310 * and we want to go into that space.
2311 * Should not happen in the real world (tm)
2312 * and is untested....
2313 */
2314
2315 /* make sure the aligned new mbr fits */
2316 uint mbrsec = rounddown(p_start,
2317 parts->ext_ptn_alignment);
2318 if (mbrsec <= data.start + data.size)
2319 data.size = mbrsec-1-data.start;
2320
2321 /* now the new partition data is ready,
2322 * write out to old position */
2323 new_mbr->sector = m->sector;
2324 newp = &new_mbr->mbr.mbr_parts[0];
2325 mbr_info_to_partitition(&data,
2326 &new_mbr->mbr.mbr_parts[0],
2327 new_mbr->sector, new_mbr, 0, errmsg);
2328 if (data.last_mounted && m->last_mounted[0] &&
2329 data.last_mounted != m->last_mounted[0]) {
2330 free(__UNCONST(m->last_mounted[0]));
2331 m->last_mounted[0] =
2332 strdup(data.last_mounted);
2333 }
2334 new_mbr->extended = m;
2335 } else {
2336 new_mbr->sector = max(roundup(data.start,
2337 parts->ext_ptn_alignment),
2338 parts->ext_ptn_alignment);
2339 uint off = new_mbr->sector - data.start;
2340 data.start += parts->ptn_0_offset+off;
2341 if (data.start + data.size > e_end)
2342 data.size = e_end - data.start;
2343 newp = &new_mbr->mbr.mbr_parts[0];
2344 mbr_info_to_partitition(&data,
2345 &new_mbr->mbr.mbr_parts[0],
2346 new_mbr->sector, new_mbr, 0, errmsg);
2347 if (data.last_mounted && m->last_mounted[0] &&
2348 data.last_mounted != m->last_mounted[0]) {
2349 free(__UNCONST(m->last_mounted[0]));
2350 m->last_mounted[0] =
2351 strdup(data.last_mounted);
2352 }
2353 /*
2354 * Special case: if we are creating the
2355 * first extended mbr, but do not start
2356 * at the beginning of the primary
2357 * extended partition, we need to insert
2358 * another extended mbr at the start.
2359 */
2360 if (m == &parts->mbr && m->extended == NULL
2361 && new_mbr->sector > ext_start) {
2362 t = calloc(1, sizeof *new_mbr);
2363 if (!t) {
2364 free_mbr_info(new_mbr);
2365 if (errmsg)
2366 *errmsg = err_outofmem;
2367 return NO_PART;
2368 }
2369 t->sector = ext_start;
2370 t->mbr.mbr_magic = htole16(MBR_MAGIC);
2371 t->mbr.mbr_parts[1].mbrp_type =
2372 MBR_PTYPE_EXT;
2373 m->extended = t;
2374 m = t;
2375 }
2376 new_mbr->extended = m->extended;
2377 m->extended = new_mbr;
2378 }
2379 }
2380 mbr_fixup_ext_chain(&parts->mbr, ext_start, ext_start+ext_size);
2381 dump_mbr(&parts->mbr, "after adding in extended");
2382 goto find_rval;
2383 }
2384
2385 /* this one is for the primary boot block */
2386 if (free_primary > MBR_PART_COUNT) {
2387 if (errmsg != NULL)
2388 *errmsg = ext_size > 0 ?
2389 MSG_mbr_no_free_primary_have_ext
2390 : MSG_mbr_no_free_primary_no_ext;
2391 return NO_PART;
2392 }
2393
2394 start = max(info->start, parts->ptn_0_offset);
2395 size = info->size;
2396 if (find_mbr_space(m, &start, &size, start, parts->dp.disk_size,
2397 start, true) < 0 || size < info->size) {
2398 if (errmsg != NULL)
2399 *errmsg = MSG_No_free_space;
2400 return NO_PART;
2401 }
2402 data.start = start;
2403 if (MBR_IS_EXTENDED(mbr_type_from_gen_desc(info->nat_type))) {
2404 data.start = max(roundup(data.start, parts->ext_ptn_alignment),
2405 parts->ext_ptn_alignment);
2406 }
2407 if (data.start + data.size > start + size)
2408 data.size = start + size - data.start;
2409 mbr_info_to_partitition(&data, &m->mbr.mbr_parts[free_primary],
2410 m->sector, m, free_primary, errmsg);
2411 if (data.last_mounted && m->last_mounted[free_primary] &&
2412 data.last_mounted != m->last_mounted[free_primary]) {
2413 free(__UNCONST(m->last_mounted[free_primary]));
2414 m->last_mounted[free_primary] = strdup(data.last_mounted);
2415 }
2416 start = m->mbr.mbr_parts[free_primary].mbrp_start;
2417 mbr_sort_main_mbr(&m->mbr);
2418
2419 /* find the partition again after sorting */
2420 newp = NULL;
2421 for (i = 0; i < MBR_PART_COUNT; i++) {
2422 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2423 continue;
2424 if (m->mbr.mbr_parts[i].mbrp_start != start)
2425 continue;
2426 newp = &m->mbr.mbr_parts[i];
2427 break;
2428 }
2429
2430 dump_mbr(&parts->mbr, "after adding in primary");
2431
2432 find_rval:
2433 mbr_calc_free_space(parts);
2434 if (newp == NULL)
2435 return 0;
2436
2437 /*
2438 * Now newp points to the modified partition entry but we do not know
2439 * a good part_id for it.
2440 * Iterate from start and find it.
2441 */
2442 no = 0;
2443 for (i = 0; i < MBR_PART_COUNT; i++) {
2444 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2445 continue;
2446
2447 if (newp == &m->mbr.mbr_parts[i])
2448 return no;
2449 no++;
2450
2451 if (MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type)) {
2452 for (me = m->extended; me != NULL; me = me->extended) {
2453 for (j = 0; j < MBR_PART_COUNT; j++) {
2454 if (me->mbr.mbr_parts[j].mbrp_type ==
2455 MBR_PTYPE_UNUSED)
2456 continue;
2457 if (j > 0 && MBR_IS_EXTENDED(
2458 me->mbr.mbr_parts[j].mbrp_type))
2459 break;
2460 if (newp == &me->mbr.mbr_parts[j])
2461 return no;
2462 no++;
2463 }
2464 }
2465 }
2466 }
2467 return 0;
2468 }
2469
2470 static int
mbr_guess_geom(struct disk_partitions * arg,int * cyl,int * head,int * sec)2471 mbr_guess_geom(struct disk_partitions *arg, int *cyl, int *head, int *sec)
2472 {
2473 struct mbr_disk_partitions *myparts = (struct mbr_disk_partitions*)arg;
2474 struct mbr_sector *mbrs = &myparts->mbr.mbr;
2475 struct mbr_partition *parts = &mbrs->mbr_parts[0];
2476 int xcylinders, xheads, i, j;
2477 daddr_t xsectors, xsize;
2478 int c1, h1, s1, c2, h2, s2;
2479 daddr_t a1, a2;
2480 uint64_t num, denom;
2481
2482 xheads = -1;
2483
2484 /* Try to deduce the number of heads from two different mappings. */
2485 for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
2486 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
2487 continue;
2488 a1 -= s1;
2489 for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
2490 if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
2491 continue;
2492 a2 -= s2;
2493 num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
2494 denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
2495 if (num != 0 && denom != 0 && num % denom == 0) {
2496 xheads = (int)(num / denom);
2497 xsectors = a1 / (c1 * xheads + h1);
2498 break;
2499 }
2500 }
2501 if (xheads != -1)
2502 break;
2503 }
2504
2505 if (xheads == -1)
2506 return -1;
2507
2508 /*
2509 * Estimate the number of cylinders.
2510 * XXX relies on get_disks having been called.
2511 */
2512 xsize = min(pm->dlsize, mbr_parts.size_limit);
2513 xcylinders = xsize / xheads / xsectors;
2514 if (xsize != xcylinders * xheads * xsectors)
2515 xcylinders++;
2516
2517 /*
2518 * Now verify consistency with each of the partition table entries.
2519 * Be willing to shove cylinders up a little bit to make things work,
2520 * but translation mismatches are fatal.
2521 */
2522 for (i = 0; i < MBR_PART_COUNT * 2; i++) {
2523 if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
2524 continue;
2525 if (c1 >= MAXCYL - 1)
2526 /* Ignore anything that is near the CHS limit */
2527 continue;
2528 if (xsectors * (c1 * xheads + h1) + s1 != a1)
2529 return -1;
2530 }
2531
2532 /*
2533 * Everything checks out. Reset the geometry to use for further
2534 * calculations.
2535 */
2536 *cyl = MIN(xcylinders, MAXCYL);
2537 *head = xheads;
2538 *sec = xsectors;
2539 return 0;
2540 }
2541
2542 static size_t
mbr_get_cylinder(const struct disk_partitions * arg)2543 mbr_get_cylinder(const struct disk_partitions *arg)
2544 {
2545 const struct mbr_disk_partitions *parts =
2546 (const struct mbr_disk_partitions*)arg;
2547
2548 return parts->geo_cyl;
2549 }
2550
2551 static daddr_t
mbr_max_part_size(const struct disk_partitions * arg,daddr_t fp_start)2552 mbr_max_part_size(const struct disk_partitions *arg, daddr_t fp_start)
2553 {
2554 const struct mbr_disk_partitions *parts =
2555 (const struct mbr_disk_partitions*)arg;
2556 uint start = fp_start, size = 0;
2557 uint8_t pt;
2558
2559 start = fp_start;
2560 pt = mbr_type_from_start(&parts->mbr, start);
2561 if (find_mbr_space(&parts->mbr, &start, &size, start,
2562 parts->dp.disk_size, start, MBR_IS_EXTENDED(pt)) < 0)
2563 return 0;
2564
2565 return size;
2566 }
2567
2568 static size_t
mbr_get_free_spaces(const struct disk_partitions * arg,struct disk_part_free_space * result,size_t max_num_result,daddr_t min_size,daddr_t align,daddr_t lower_bound,daddr_t ignore)2569 mbr_get_free_spaces(const struct disk_partitions *arg,
2570 struct disk_part_free_space *result, size_t max_num_result,
2571 daddr_t min_size, daddr_t align, daddr_t lower_bound, daddr_t ignore)
2572 {
2573 const struct mbr_disk_partitions *parts =
2574 (const struct mbr_disk_partitions*)arg;
2575 uint start = 0, size = 0, from, next;
2576 size_t spaces = 0;
2577
2578 if (min_size < 1)
2579 min_size = 1;
2580 from = parts->ptn_0_offset;
2581 if (lower_bound > from)
2582 from = lower_bound;
2583 for ( ; from < parts->dp.disk_size && spaces < max_num_result; ) {
2584 if (find_mbr_space(&parts->mbr, &start, &size, from,
2585 parts->dp.disk_size, ignore > 0 ? (uint)ignore : UINT_MAX,
2586 false) < 0)
2587 break;
2588 next = start + size + 1;
2589 if (align > 0) {
2590 uint nv = max(roundup(start, align), align);
2591 uint off = nv - start;
2592 start = nv;
2593 if (size > off)
2594 size -= off;
2595 else
2596 size = 0;
2597 }
2598 if (size > min_size) {
2599 result[spaces].start = start;
2600 result[spaces].size = size;
2601 spaces++;
2602 }
2603 if ((daddr_t)start + (daddr_t)size + 1 >= mbr_parts.size_limit)
2604 break;
2605 from = next;
2606 }
2607
2608 return spaces;
2609 }
2610
2611 static bool
mbr_can_add_partition(const struct disk_partitions * arg)2612 mbr_can_add_partition(const struct disk_partitions *arg)
2613 {
2614 const struct mbr_disk_partitions *myparts =
2615 (const struct mbr_disk_partitions*)arg;
2616 struct disk_part_free_space space;
2617 bool free_primary, have_extended;
2618
2619 if (arg->free_space < myparts->ptn_alignment)
2620 return false;
2621
2622 if (mbr_get_free_spaces(arg, &space, 1, myparts->ptn_alignment,
2623 myparts->ptn_alignment, 0, -1) < 1)
2624 return false;
2625
2626 for (int i = 0; i < MBR_PART_COUNT; i++) {
2627 uint8_t t = myparts->mbr.mbr.mbr_parts[i].mbrp_type;
2628
2629 if (t == MBR_PTYPE_UNUSED &&
2630 myparts->mbr.mbr.mbr_parts[i].mbrp_size == 0)
2631 free_primary = true;
2632
2633 if (MBR_IS_EXTENDED(t))
2634 have_extended = true;
2635 }
2636
2637 if (have_extended)
2638 return true;
2639
2640 return free_primary;
2641 }
2642
2643 static void
mbr_free_wedge(int * fd,const char * disk,const char * wedge)2644 mbr_free_wedge(int *fd, const char *disk, const char *wedge)
2645 {
2646 struct dkwedge_info dkw;
2647 char diskpath[MAXPATHLEN];
2648
2649 if (*fd == -1)
2650 *fd = opendisk(disk, O_RDWR, diskpath,
2651 sizeof(diskpath), 0);
2652 if (*fd != -1) {
2653 memset(&dkw, 0, sizeof(dkw));
2654 strlcpy(dkw.dkw_devname, wedge,
2655 sizeof(dkw.dkw_devname));
2656 ioctl(*fd, DIOCDWEDGE, &dkw);
2657 }
2658 }
2659
2660 static void
mbr_free(struct disk_partitions * arg)2661 mbr_free(struct disk_partitions *arg)
2662 {
2663 struct mbr_disk_partitions *parts = (struct mbr_disk_partitions*)arg;
2664 mbr_info_t *m;
2665 int i, fd;
2666
2667 assert(parts != NULL);
2668
2669 fd = -1;
2670 m = &parts->mbr;
2671 do {
2672 for (i = 0; i < MBR_PART_COUNT; i++) {
2673 if (m->wedge[i][0] != 0)
2674 mbr_free_wedge(&fd, arg->disk, m->wedge[i]);
2675 }
2676 } while ((m = m->extended));
2677
2678 if (fd != -1)
2679 close(fd);
2680
2681 if (parts->dlabel)
2682 parts->dlabel->pscheme->free(parts->dlabel);
2683
2684 free_mbr_info(parts->mbr.extended);
2685 free_last_mounted(&parts->mbr);
2686 free(__UNCONST(parts->dp.disk));
2687 free(parts);
2688 }
2689
2690 static void
mbr_destroy_part_scheme(struct disk_partitions * arg)2691 mbr_destroy_part_scheme(struct disk_partitions *arg)
2692 {
2693 struct mbr_disk_partitions *parts = (struct mbr_disk_partitions*)arg;
2694 char diskpath[MAXPATHLEN];
2695 int fd;
2696
2697 if (parts->dlabel != NULL)
2698 parts->dlabel->pscheme->destroy_part_scheme(parts->dlabel);
2699 fd = opendisk(arg->disk, O_RDWR, diskpath, sizeof(diskpath), 0);
2700 if (fd != -1) {
2701 char *buf;
2702
2703 buf = calloc(arg->bytes_per_sector, 1);
2704 if (buf != NULL) {
2705 write(fd, buf, arg->bytes_per_sector);
2706 free(buf);
2707 }
2708 close(fd);
2709 }
2710 mbr_free(arg);
2711 }
2712
2713 static bool
mbr_verify_for_update(struct disk_partitions * arg)2714 mbr_verify_for_update(struct disk_partitions *arg)
2715 {
2716 struct mbr_disk_partitions *parts =
2717 (struct mbr_disk_partitions*)arg;
2718
2719 return md_mbr_update_check(arg, &parts->mbr);
2720 }
2721
2722 static int
mbr_verify(struct disk_partitions * arg,bool quiet)2723 mbr_verify(struct disk_partitions *arg, bool quiet)
2724 {
2725 struct mbr_disk_partitions *parts =
2726 (struct mbr_disk_partitions*)arg;
2727 mbr_info_t *m = &parts->mbr;
2728 int i;
2729 bool active_found = false;
2730
2731 for (i = 0; i < MBR_PART_COUNT; i++) {
2732 if (m->mbr.mbr_parts[i].mbrp_flag & MBR_PFLAG_ACTIVE) {
2733 active_found = true;
2734 break;
2735 }
2736 }
2737
2738 if (!active_found && pm->ptstart > 0) {
2739 struct mbr_partition *mp = mbr_ptr_from_start(m, pm->ptstart);
2740
2741 if (mp) {
2742 if (!quiet)
2743 msg_display(MSG_noactivepart);
2744 if (quiet || ask_yesno(MSG_fixactivepart)) {
2745 mp->mbrp_flag |= MBR_PFLAG_ACTIVE;
2746 active_found = true;
2747 }
2748 }
2749 }
2750 if (!active_found && !quiet) {
2751 msg_display(MSG_noactivepart);
2752 i = ask_reedit(arg);
2753 if (i <= 1)
2754 return i;
2755 }
2756
2757 for (i = 0; i < MBR_PART_COUNT; i++) {
2758 if (m->mbr.mbr_parts[i].mbrp_type != MBR_PTYPE_NETBSD)
2759 continue;
2760 m->mbr.mbr_parts[i].mbrp_flag |= MBR_PFLAG_ACTIVE;
2761 break;
2762 }
2763
2764 return md_check_mbr(arg, &parts->mbr, quiet);
2765 }
2766
2767 static bool
mbr_guess_root(const struct disk_partitions * arg,daddr_t * start,daddr_t * size)2768 mbr_guess_root(const struct disk_partitions *arg,
2769 daddr_t *start, daddr_t *size)
2770 {
2771 const struct mbr_disk_partitions *parts =
2772 (const struct mbr_disk_partitions*)arg;
2773 const mbr_info_t *m = &parts->mbr;
2774 size_t i, num_found;
2775 bool prim = true;
2776 daddr_t pstart, psize;
2777
2778 num_found = 0;
2779 do {
2780 for (i = 0; i < MBR_PART_COUNT; i++) {
2781 if (m->mbr.mbr_parts[i].mbrp_type == MBR_PTYPE_UNUSED)
2782 continue;
2783
2784 if (!prim && i > 0 &&
2785 MBR_IS_EXTENDED(m->mbr.mbr_parts[i].mbrp_type))
2786 break;
2787
2788 const struct mbr_partition *mp = &m->mbr.mbr_parts[i];
2789 if (mp->mbrp_type != MBR_PTYPE_NETBSD)
2790 continue;
2791
2792 if (num_found == 0) {
2793 pstart = m->sector + mp->mbrp_start;
2794 psize = mp->mbrp_size;
2795 }
2796 num_found++;
2797
2798 if (m->last_mounted[i] != NULL &&
2799 strcmp(m->last_mounted[i], "/") == 0) {
2800 *start = pstart;
2801 *size = psize;
2802 return true;
2803 }
2804 }
2805 prim = false;
2806 } while ((m = m->extended));
2807
2808 if (num_found == 1) {
2809 *start = pstart;
2810 *size = psize;
2811 return true;
2812 }
2813
2814 return false;
2815 }
2816
2817 struct part_attr_fmt_data {
2818 char *str;
2819 size_t avail_space, attr_no;
2820 const struct mbr_disk_partitions *parts;
2821 const struct disk_part_info *info;
2822 };
2823
2824 struct part_attr_set_data {
2825 size_t attr_no;
2826 const struct mbr_disk_partitions *parts;
2827 const char *str;
2828 mbr_info_t *mbr;
2829 };
2830
2831 static bool
part_attr_format_str(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)2832 part_attr_format_str(const struct disk_partitions *arg, part_id id,
2833 const mbr_info_t *mb, int i, bool primary,
2834 const struct mbr_partition *mp, void *cookie)
2835 {
2836 const struct mbr_disk_partitions *parts =
2837 (const struct mbr_disk_partitions*)arg;
2838 struct part_attr_fmt_data *data = cookie;
2839 const char *attrtype = parts->dp.pscheme
2840 ->custom_attributes[data->attr_no].label;
2841
2842 if (attrtype == MSG_ptn_active) {
2843 strlcpy(data->str,
2844 msg_string(primary && (mp->mbrp_flag & MBR_PFLAG_ACTIVE) ?
2845 MSG_Yes : MSG_No), data->avail_space);
2846 return true;
2847 #if BOOTSEL
2848 } else if (attrtype == MSG_boot_dflt) {
2849 strlcpy(data->str,
2850 msg_string(
2851 (parts->mbr.bootsec == mb->sector+mp->mbrp_start) ?
2852 MSG_Yes : MSG_No), data->avail_space);
2853 return true;
2854 } else if (attrtype == MSG_bootmenu) {
2855 strlcpy(data->str, mb->mbrb.mbrbs_nametab[i],
2856 data->avail_space);
2857 #endif
2858 }
2859
2860 return false;
2861 }
2862
2863 static bool
part_attr_set_str(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)2864 part_attr_set_str(const struct disk_partitions *arg, part_id id,
2865 const mbr_info_t *mb, int i, bool primary,
2866 const struct mbr_partition *mp, void *cookie)
2867 {
2868 struct part_attr_set_data *data = cookie;
2869 const char *str = data->str;
2870 #ifdef BOOTSEL
2871 const struct mbr_disk_partitions *parts =
2872 (const struct mbr_disk_partitions*)arg;
2873 const char *attrtype = parts->dp.pscheme
2874 ->custom_attributes[data->attr_no].label;
2875 mbr_info_t *m;
2876 #endif
2877
2878 while (*str == ' ')
2879 str++;
2880
2881 #if BOOTSEL
2882 if (attrtype == MSG_bootmenu) {
2883 for (m = data->mbr; m != mb; m = m->extended)
2884 ;
2885 strncpy(m->mbrb.mbrbs_nametab[i], str,
2886 sizeof(m->mbrb.mbrbs_nametab[i]));
2887 }
2888 #endif
2889
2890 return false;
2891 }
2892
2893 static bool
part_attr_toggle(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)2894 part_attr_toggle(const struct disk_partitions *arg, part_id id,
2895 const mbr_info_t *mb, int i, bool primary,
2896 const struct mbr_partition *mp, void *cookie)
2897 {
2898 const struct mbr_disk_partitions *parts =
2899 (const struct mbr_disk_partitions*)arg;
2900 struct part_attr_set_data *data = cookie;
2901 const char *attrtype = parts->dp.pscheme
2902 ->custom_attributes[data->attr_no].label;
2903 int j;
2904
2905 if (attrtype == MSG_ptn_active) {
2906 if (!primary)
2907 return false;
2908
2909 data->mbr->mbr.mbr_parts[i].mbrp_flag ^= MBR_PFLAG_ACTIVE;
2910 for (j = 0; j < MBR_PART_COUNT; j++) {
2911 if (j == i)
2912 continue;
2913 data->mbr->mbr.mbr_parts[j].mbrp_flag
2914 &= ~MBR_PFLAG_ACTIVE;
2915 }
2916 return true;
2917 #ifdef BOOTSEL
2918 } else if (attrtype == MSG_boot_dflt) {
2919 if (data->mbr->bootsec == mb->sector+mp->mbrp_start)
2920 data->mbr->bootsec = 0;
2921 else
2922 data->mbr->bootsec = mb->sector+mp->mbrp_start;
2923 return true;
2924 #endif
2925 }
2926
2927 return false;
2928 }
2929
2930 static bool
mbr_custom_attribute_format(const struct disk_partitions * arg,part_id id,size_t attr_no,const struct disk_part_info * info,char * res,size_t space)2931 mbr_custom_attribute_format(const struct disk_partitions *arg,
2932 part_id id, size_t attr_no, const struct disk_part_info *info,
2933 char *res, size_t space)
2934 {
2935 const struct mbr_disk_partitions *parts =
2936 (const struct mbr_disk_partitions*)arg;
2937 struct part_attr_fmt_data data;
2938
2939 data.str = res;
2940 data.avail_space = space;
2941 data.attr_no = attr_no;
2942 data.parts = parts;
2943 data.info = info;
2944
2945 return mbr_part_apply(arg, id, part_attr_format_str, &data);
2946 }
2947
2948 static bool
mbr_custom_attribute_toggle(struct disk_partitions * arg,part_id id,size_t attr_no)2949 mbr_custom_attribute_toggle(struct disk_partitions *arg,
2950 part_id id, size_t attr_no)
2951 {
2952 struct mbr_disk_partitions *parts =
2953 (struct mbr_disk_partitions*)arg;
2954 struct part_attr_set_data data;
2955
2956 data.attr_no = attr_no;
2957 data.parts = parts;
2958 data.str = NULL;
2959 #ifdef BOOTSEL
2960 data.mbr = &parts->mbr;
2961 #endif
2962
2963 return mbr_part_apply(arg, id, part_attr_toggle, &data);
2964 }
2965
2966 static bool
mbr_custom_attribute_set_str(struct disk_partitions * arg,part_id id,size_t attr_no,const char * new_val)2967 mbr_custom_attribute_set_str(struct disk_partitions *arg,
2968 part_id id, size_t attr_no, const char *new_val)
2969 {
2970 struct mbr_disk_partitions *parts =
2971 (struct mbr_disk_partitions*)arg;
2972 struct part_attr_set_data data;
2973
2974 data.attr_no = attr_no;
2975 data.parts = parts;
2976 data.str = new_val;
2977 #ifdef BOOTSEL
2978 data.mbr = &parts->mbr;
2979 #endif
2980
2981 return mbr_part_apply(arg, id, part_attr_set_str, &data);
2982 }
2983
2984 static daddr_t
mbr_part_alignment(const struct disk_partitions * arg)2985 mbr_part_alignment(const struct disk_partitions *arg)
2986 {
2987 const struct mbr_disk_partitions *parts =
2988 (const struct mbr_disk_partitions*)arg;
2989
2990 return parts->ptn_alignment;
2991 }
2992
2993 static bool
add_wedge(const char * disk,daddr_t start,daddr_t size,char * wname,size_t max_len)2994 add_wedge(const char *disk, daddr_t start, daddr_t size,
2995 char *wname, size_t max_len)
2996 {
2997 struct dkwedge_info dkw;
2998 char diskpath[MAXPATHLEN];
2999 int fd;
3000
3001 memset(&dkw, 0, sizeof(dkw));
3002 dkw.dkw_offset = start;
3003 dkw.dkw_size = size;
3004 snprintf((char*)dkw.dkw_wname, sizeof dkw.dkw_wname,
3005 "%s_%" PRIi64 "@%" PRIi64, disk, size, start);
3006
3007 *wname = 0;
3008
3009 fd = opendisk(disk, O_RDWR, diskpath, sizeof(diskpath), 0);
3010 if (fd < 0)
3011 return false;
3012 if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) {
3013 close(fd);
3014 return false;
3015 }
3016 close(fd);
3017 strlcpy(wname, dkw.dkw_devname, max_len);
3018 return true;
3019 }
3020
3021 static bool
mbr_get_part_device(const struct disk_partitions * arg,part_id ptn,char * devname,size_t max_devname_len,int * part,enum dev_name_usage usage,bool with_path,bool life)3022 mbr_get_part_device(const struct disk_partitions *arg,
3023 part_id ptn, char *devname, size_t max_devname_len, int *part,
3024 enum dev_name_usage usage, bool with_path, bool life)
3025 {
3026 const struct mbr_disk_partitions *parts =
3027 (const struct mbr_disk_partitions*)arg;
3028 struct disk_part_info info, tmp;
3029 part_id dptn;
3030 char *wedge_dev;
3031
3032 if (!mbr_get_part_info(arg, ptn, &info))
3033 return false;
3034
3035 if (!mbr_part_get_wedge(arg, ptn, &wedge_dev) || wedge_dev == NULL)
3036 return false;
3037
3038 if (wedge_dev[0] == 0) {
3039 /*
3040 * If we have secondary partitions, try to find a match there
3041 * and use that...
3042 */
3043 if (parts->dlabel != NULL) {
3044 for (dptn = 0; dptn < parts->dlabel->num_part; dptn++) {
3045 if (!parts->dlabel->pscheme->get_part_info(
3046 parts->dlabel, dptn, &tmp))
3047 continue;
3048 if (tmp.start != info.start ||
3049 tmp.size != info.size)
3050 continue;
3051 return parts->dlabel->pscheme->get_part_device(
3052 parts->dlabel, dptn, devname,
3053 max_devname_len,
3054 part, usage, with_path, life);
3055 }
3056 }
3057
3058 /*
3059 * Configure a new wedge and remember the name
3060 */
3061 if (!add_wedge(arg->disk, info.start, info.size, wedge_dev,
3062 MBR_DEV_LEN))
3063 return false;
3064 }
3065
3066 assert(wedge_dev[0] != 0);
3067
3068 switch (usage) {
3069 case logical_name:
3070 case plain_name:
3071 if (with_path)
3072 snprintf(devname, max_devname_len, _PATH_DEV "%s",
3073 wedge_dev);
3074 else
3075 strlcpy(devname, wedge_dev, max_devname_len);
3076 return true;
3077 case raw_dev_name:
3078 if (with_path)
3079 snprintf(devname, max_devname_len, _PATH_DEV "r%s",
3080 wedge_dev);
3081 else
3082 snprintf(devname, max_devname_len, "r%s",
3083 wedge_dev);
3084 return true;
3085 default:
3086 return false;
3087 }
3088 }
3089
3090 static bool
is_custom_attribute_writable(const struct disk_partitions * arg,part_id id,const mbr_info_t * mb,int i,bool primary,const struct mbr_partition * mp,void * cookie)3091 is_custom_attribute_writable(const struct disk_partitions *arg, part_id id,
3092 const mbr_info_t *mb, int i, bool primary,
3093 const struct mbr_partition *mp, void *cookie)
3094 {
3095 const struct mbr_disk_partitions *parts =
3096 (const struct mbr_disk_partitions*)arg;
3097 struct part_attr_set_data *data = cookie;
3098 const char *attrtype = parts->dp.pscheme
3099 ->custom_attributes[data->attr_no].label;
3100
3101 if (attrtype == MSG_ptn_active)
3102 /* Only 'normal' partitions can be 'Active' */
3103 return primary && !MBR_IS_EXTENDED(mp->mbrp_type);
3104 #ifdef BOOTSEL
3105 else if (attrtype == MSG_boot_dflt)
3106 /* Only partitions with bootmenu names can be default */
3107 return mb->mbrb.mbrbs_nametab[i][0] != 0;
3108 else if (attrtype == MSG_bootmenu)
3109 /* The extended partition isn't bootable */
3110 return !MBR_IS_EXTENDED(mp->mbrp_type);
3111 #endif
3112
3113 return false;
3114 }
3115
3116 static bool
mbr_custom_attribute_writable(const struct disk_partitions * arg,part_id id,size_t attr_no)3117 mbr_custom_attribute_writable(const struct disk_partitions *arg,
3118 part_id id, size_t attr_no)
3119 {
3120 const struct mbr_disk_partitions *parts =
3121 (const struct mbr_disk_partitions*)arg;
3122 struct part_attr_set_data data;
3123
3124 data.attr_no = attr_no;
3125 data.parts = parts;
3126 data.str = NULL;
3127 #ifdef BOOTSEL
3128 data.mbr = NULL;
3129 #endif
3130
3131 return mbr_part_apply(arg, id, is_custom_attribute_writable, &data);
3132 }
3133
3134 const struct disk_part_edit_column_desc mbr_edit_columns[] = {
3135 { .title = MSG_mbr_part_header_1,
3136 #if BOOTSEL
3137 .width = 16U
3138 #else
3139 .width = 26U
3140 #endif
3141 },
3142 { .title = MSG_mbr_part_header_2, .width = 8U },
3143 #if BOOTSEL
3144 { .title = MSG_mbr_part_header_3, .width = 9U },
3145 #endif
3146 };
3147
3148 const struct disk_part_custom_attribute mbr_custom_attrs[] = {
3149 { .label = MSG_ptn_active, .type = pet_bool },
3150 #if BOOTSEL
3151 { .label = MSG_boot_dflt, .type = pet_bool },
3152 { .label = MSG_bootmenu, .type = pet_str,
3153 .strlen = MBR_BS_PARTNAMESIZE },
3154 #endif
3155 };
3156
3157 const struct disk_partitioning_scheme
3158 mbr_parts = {
3159 .name = MSG_parttype_mbr,
3160 .short_name = MSG_parttype_mbr_short,
3161 .new_type_prompt = MSG_mbr_get_ptn_id,
3162 .part_flag_desc = MSG_mbr_flag_desc,
3163 .size_limit = (daddr_t)UINT32_MAX,
3164 .secondary_scheme = &disklabel_parts,
3165 .edit_columns_count = __arraycount(mbr_edit_columns),
3166 .edit_columns = mbr_edit_columns,
3167 .custom_attribute_count = __arraycount(mbr_custom_attrs),
3168 .custom_attributes = mbr_custom_attrs,
3169 .get_part_alignment = mbr_part_alignment,
3170 .get_part_info = mbr_get_part_info,
3171 .get_part_attr_str = mbr_part_attr_str,
3172 .format_partition_table_str = mbr_table_str,
3173 .part_type_can_change = mbr_part_type_can_change,
3174 .can_add_partition = mbr_can_add_partition,
3175 .custom_attribute_writable = mbr_custom_attribute_writable,
3176 .format_custom_attribute = mbr_custom_attribute_format,
3177 .custom_attribute_toggle = mbr_custom_attribute_toggle,
3178 .custom_attribute_set_str = mbr_custom_attribute_set_str,
3179 .get_part_types_count = mbr_get_part_type_count,
3180 .adapt_foreign_part_info = generic_adapt_foreign_part_info,
3181 .get_part_type = mbr_get_part_type,
3182 .get_fs_part_type = mbr_get_fs_part_type,
3183 .get_generic_part_type = mbr_get_generic_part_type,
3184 .create_custom_part_type = mbr_custom_part_type,
3185 .create_unknown_part_type = mbr_create_unknown_part_type,
3186 .secondary_partitions = mbr_read_disklabel,
3187 .write_to_disk = mbr_write_to_disk,
3188 .read_from_disk = mbr_read_from_disk,
3189 .create_new_for_disk = mbr_create_new,
3190 .guess_disk_geom = mbr_guess_geom,
3191 .get_cylinder_size = mbr_get_cylinder,
3192 .change_disk_geom = mbr_change_disk_geom,
3193 .get_part_device = mbr_get_part_device,
3194 .max_free_space_at = mbr_max_part_size,
3195 .get_free_spaces = mbr_get_free_spaces,
3196 .set_part_info = mbr_set_part_info,
3197 .delete_all_partitions = mbr_delete_all,
3198 .delete_partition = mbr_delete_part,
3199 .add_partition = mbr_add_part,
3200 .guess_install_target = mbr_guess_root,
3201 .post_edit_verify = mbr_verify,
3202 .pre_update_verify = mbr_verify_for_update,
3203 .free = mbr_free,
3204 .destroy_part_scheme = mbr_destroy_part_scheme,
3205 };
3206
3207 #endif
3208