]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sbin/fdisk/fdisk.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sbin / fdisk / fdisk.c
1 /*
2  * Mach Operating System
3  * Copyright (c) 1992 Carnegie Mellon University
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  *
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  *
16  * Carnegie Mellon requests users of this software to return to
17  *
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  *
23  * any improvements or extensions that they make and grant Carnegie Mellon
24  * the rights to redistribute these changes.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/disk.h>
31 #include <sys/disklabel.h>
32 #include <sys/diskmbr.h>
33 #include <sys/endian.h>
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #include <sys/mount.h>
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <libgeom.h>
42 #include <paths.h>
43 #include <regex.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 int iotest;
51
52 #define NOSECTORS ((u_int32_t)-1)
53 #define LBUF 100
54 static char lbuf[LBUF];
55
56 /*
57  *
58  * Ported to 386bsd by Julian Elischer  Thu Oct 15 20:26:46 PDT 1992
59  *
60  * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
61  *      Copyright (c) 1989      Robert. V. Baron
62  *      Created.
63  */
64
65 #define Decimal(str, ans, tmp) if (decimal(str, &tmp, ans)) ans = tmp
66
67 #define RoundCyl(x) ((((x) + cylsecs - 1) / cylsecs) * cylsecs)
68
69 #define MAX_SEC_SIZE 2048       /* maximum section size that is supported */
70 #define MIN_SEC_SIZE 512        /* the sector size to start sensing at */
71 static int secsize = 0;         /* the sensed sector size */
72
73 static char *disk;
74
75 static int cyls, sectors, heads, cylsecs, disksecs;
76
77 struct mboot {
78         unsigned char *bootinst;  /* boot code */
79         off_t bootinst_size;
80         struct  dos_partition parts[NDOSPART];
81 };
82
83 static struct mboot mboot;
84 static int fd;
85
86 #define ACTIVE 0x80
87
88 static uint dos_cyls;
89 static uint dos_heads;
90 static uint dos_sectors;
91 static uint dos_cylsecs;
92
93 #define DOSSECT(s,c) ((s & 0x3f) | ((c >> 2) & 0xc0))
94 #define DOSCYL(c)       (c & 0xff)
95
96 #define MAX_ARGS        10
97
98 static int      current_line_number;
99
100 static int      geom_processed = 0;
101 static int      part_processed = 0;
102 static int      active_processed = 0;
103
104 typedef struct cmd {
105     char                cmd;
106     int                 n_args;
107     struct arg {
108         char    argtype;
109         int     arg_val;
110         char    *arg_str;
111     }                   args[MAX_ARGS];
112 } CMD;
113
114 static int B_flag  = 0;         /* replace boot code */
115 static int I_flag  = 0;         /* use entire disk for FreeBSD */
116 static int a_flag  = 0;         /* set active partition */
117 static char *b_flag = NULL;     /* path to boot code */
118 static int i_flag  = 0;         /* replace partition data */
119 static int q_flag  = 0;         /* Be quiet */
120 static int u_flag  = 0;         /* update partition data */
121 static int s_flag  = 0;         /* Print a summary and exit */
122 static int t_flag  = 0;         /* test only */
123 static char *f_flag = NULL;     /* Read config info from file */
124 static int v_flag  = 0;         /* Be verbose */
125 static int print_config_flag = 0;
126
127 /*
128  * A list of partition types, probably outdated.
129  */
130 static const char *const part_types[256] = {
131         [0x00] = "unused",
132         [0x01] = "Primary DOS with 12 bit FAT",
133         [0x02] = "XENIX / file system",
134         [0x03] = "XENIX /usr file system",
135         [0x04] = "Primary DOS with 16 bit FAT (< 32MB)",
136         [0x05] = "Extended DOS",
137         [0x06] = "Primary DOS, 16 bit FAT (>= 32MB)",
138         [0x07] = "NTFS, OS/2 HPFS, QNX-2 (16 bit) or Advanced UNIX",
139         [0x08] = "AIX file system or SplitDrive",
140         [0x09] = "AIX boot partition or Coherent",
141         [0x0A] = "OS/2 Boot Manager, OPUS or Coherent swap",
142         [0x0B] = "DOS or Windows 95 with 32 bit FAT",
143         [0x0C] = "DOS or Windows 95 with 32 bit FAT (LBA)",
144         [0x0E] = "Primary 'big' DOS (>= 32MB, LBA)",
145         [0x0F] = "Extended DOS (LBA)",
146         [0x10] = "OPUS",
147         [0x11] = "OS/2 BM: hidden DOS with 12-bit FAT",
148         [0x12] = "Compaq diagnostics",
149         [0x14] = "OS/2 BM: hidden DOS with 16-bit FAT (< 32MB)",
150         [0x16] = "OS/2 BM: hidden DOS with 16-bit FAT (>= 32MB)",
151         [0x17] = "OS/2 BM: hidden IFS (e.g. HPFS)",
152         [0x18] = "AST Windows swapfile",
153         [0x1b] = "ASUS Recovery partition (NTFS)",
154         [0x24] = "NEC DOS",
155         [0x3C] = "PartitionMagic recovery",
156         [0x39] = "plan9",
157         [0x40] = "VENIX 286",
158         [0x41] = "Linux/MINIX (sharing disk with DRDOS)",
159         [0x42] = "SFS or Linux swap (sharing disk with DRDOS)",
160         [0x43] = "Linux native (sharing disk with DRDOS)",
161         [0x4D] = "QNX 4.2 Primary",
162         [0x4E] = "QNX 4.2 Secondary",
163         [0x4F] = "QNX 4.2 Tertiary",
164         [0x50] = "DM (disk manager)",
165         [0x51] = "DM6 Aux1 (or Novell)",
166         [0x52] = "CP/M or Microport SysV/AT",
167         [0x53] = "DM6 Aux3",
168         [0x54] = "DM6",
169         [0x55] = "EZ-Drive (disk manager)",
170         [0x56] = "Golden Bow (disk manager)",
171         [0x5c] = "Priam Edisk (disk manager)", /* according to S. Widlake */
172         [0x61] = "SpeedStor",
173         [0x63] = "System V/386 (such as ISC UNIX), GNU HURD or Mach",
174         [0x64] = "Novell Netware/286 2.xx",
175         [0x65] = "Novell Netware/386 3.xx",
176         [0x70] = "DiskSecure Multi-Boot",
177         [0x75] = "PCIX",
178         [0x77] = "QNX4.x",
179         [0x78] = "QNX4.x 2nd part",
180         [0x79] = "QNX4.x 3rd part",
181         [0x80] = "Minix until 1.4a",
182         [0x81] = "Minix since 1.4b, early Linux partition or Mitac disk manager",
183         [0x82] = "Linux swap or Solaris x86",
184         [0x83] = "Linux native",
185         [0x84] = "OS/2 hidden C: drive",
186         [0x85] = "Linux extended",
187         [0x86] = "NTFS volume set??",
188         [0x87] = "NTFS volume set??",
189         [0x93] = "Amoeba file system",
190         [0x94] = "Amoeba bad block table",
191         [0x9F] = "BSD/OS",
192         [0xA0] = "Suspend to Disk",
193         [0xA5] = "FreeBSD/NetBSD/386BSD",
194         [0xA6] = "OpenBSD",
195         [0xA7] = "NeXTSTEP",
196         [0xA9] = "NetBSD",
197         [0xAC] = "IBM JFS",
198         [0xAF] = "HFS+",
199         [0xB7] = "BSDI BSD/386 file system",
200         [0xB8] = "BSDI BSD/386 swap",
201         [0xBE] = "Solaris x86 boot",
202         [0xBF] = "Solaris x86 (new)",
203         [0xC1] = "DRDOS/sec with 12-bit FAT",
204         [0xC4] = "DRDOS/sec with 16-bit FAT (< 32MB)",
205         [0xC6] = "DRDOS/sec with 16-bit FAT (>= 32MB)",
206         [0xC7] = "Syrinx",
207         [0xDB] = "CP/M, Concurrent CP/M, Concurrent DOS or CTOS",
208         [0xDE] = "DELL Utilities - FAT filesystem",
209         [0xE1] = "DOS access or SpeedStor with 12-bit FAT extended partition",
210         [0xE3] = "DOS R/O or SpeedStor",
211         [0xE4] = "SpeedStor with 16-bit FAT extended partition < 1024 cyl.",
212         [0xEB] = "BeOS file system",
213         [0xEE] = "EFI GPT",
214         [0xEF] = "EFI System Partition",
215         [0xF1] = "SpeedStor",
216         [0xF2] = "DOS 3.3+ Secondary",
217         [0xF4] = "SpeedStor large partition",
218         [0xFE] = "SpeedStor >1024 cyl. or LANstep",
219         [0xFF] = "Xenix bad blocks table",
220 };
221
222 static const char *
223 get_type(int t)
224 {
225         const char *ret;
226
227         ret = (t >= 0 && t <= 255) ? part_types[t] : NULL;
228         return ret ? ret : "unknown";
229 }
230
231
232 static void print_s0(void);
233 static void print_part(const struct dos_partition *);
234 static void init_sector0(unsigned long start);
235 static void init_boot(void);
236 static void change_part(int i);
237 static void print_params(void);
238 static void change_active(int which);
239 static void change_code(void);
240 static void get_params_to_use(void);
241 static char *get_rootdisk(void);
242 static void dos(struct dos_partition *partp);
243 static int open_disk(int flag);
244 static ssize_t read_disk(off_t sector, void *buf);
245 static int write_disk(off_t sector, void *buf);
246 static int get_params(void);
247 static int read_s0(void);
248 static int write_s0(void);
249 static int ok(const char *str);
250 static int decimal(const char *str, int *num, int deflt);
251 static int read_config(char *config_file);
252 static void reset_boot(void);
253 static int sanitize_partition(struct dos_partition *);
254 static void usage(void);
255
256 int
257 main(int argc, char *argv[])
258 {
259         int     c, i;
260         int     partition = -1;
261         struct  dos_partition *partp;
262
263         while ((c = getopt(argc, argv, "BIab:f:ipqstuv1234")) != -1)
264                 switch (c) {
265                 case 'B':
266                         B_flag = 1;
267                         break;
268                 case 'I':
269                         I_flag = 1;
270                         break;
271                 case 'a':
272                         a_flag = 1;
273                         break;
274                 case 'b':
275                         b_flag = optarg;
276                         break;
277                 case 'f':
278                         f_flag = optarg;
279                         break;
280                 case 'i':
281                         i_flag = 1;
282                         break;
283                 case 'p':
284                         print_config_flag = 1;
285                         break;
286                 case 'q':
287                         q_flag = 1;
288                         break;
289                 case 's':
290                         s_flag = 1;
291                         break;
292                 case 't':
293                         t_flag = 1;
294                         break;
295                 case 'u':
296                         u_flag = 1;
297                         break;
298                 case 'v':
299                         v_flag = 1;
300                         break;
301                 case '1':
302                 case '2':
303                 case '3':
304                 case '4':
305                         partition = c - '0';
306                         break;
307                 default:
308                         usage();
309                 }
310         if (f_flag || i_flag)
311                 u_flag = 1;
312         if (t_flag)
313                 v_flag = 1;
314         argc -= optind;
315         argv += optind;
316
317         if (argc == 0) {
318                 disk = get_rootdisk();
319         } else {
320                 disk = g_device_path(argv[0]);
321                 if (disk == NULL)
322                         err(1, "unable to get correct path for %s", argv[0]);
323         }
324         if (open_disk(u_flag) < 0)
325                 err(1, "cannot open disk %s", disk);
326
327         /* (abu)use mboot.bootinst to probe for the sector size */
328         if ((mboot.bootinst = malloc(MAX_SEC_SIZE)) == NULL)
329                 err(1, "cannot allocate buffer to determine disk sector size");
330         if (read_disk(0, mboot.bootinst) == -1)
331                 errx(1, "could not detect sector size");
332         free(mboot.bootinst);
333         mboot.bootinst = NULL;
334
335         if (print_config_flag) {
336                 if (read_s0())
337                         err(1, "read_s0");
338
339                 printf("# %s\n", disk);
340                 printf("g c%d h%d s%d\n", dos_cyls, dos_heads, dos_sectors);
341
342                 for (i = 0; i < NDOSPART; i++) {
343                         partp = &mboot.parts[i];
344
345                         if (partp->dp_start == 0 && partp->dp_size == 0)
346                                 continue;
347
348                         printf("p %d 0x%02x %lu %lu\n", i + 1, partp->dp_typ,
349                             (u_long)partp->dp_start, (u_long)partp->dp_size);
350
351                         /* Fill flags for the partition. */
352                         if (partp->dp_flag & 0x80)
353                                 printf("a %d\n", i + 1);
354                 }
355                 exit(0);
356         }
357         if (s_flag) {
358                 if (read_s0())
359                         err(1, "read_s0");
360                 printf("%s: %d cyl %d hd %d sec\n", disk, dos_cyls, dos_heads,
361                     dos_sectors);
362                 printf("Part  %11s %11s Type Flags\n", "Start", "Size");
363                 for (i = 0; i < NDOSPART; i++) {
364                         partp = &mboot.parts[i];
365                         if (partp->dp_start == 0 && partp->dp_size == 0)
366                                 continue;
367                         printf("%4d: %11lu %11lu 0x%02x 0x%02x\n", i + 1,
368                             (u_long) partp->dp_start,
369                             (u_long) partp->dp_size, partp->dp_typ,
370                             partp->dp_flag);
371                 }
372                 exit(0);
373         }
374
375         printf("******* Working on device %s *******\n",disk);
376
377         if (I_flag) {
378                 read_s0();
379                 reset_boot();
380                 partp = &mboot.parts[0];
381                 partp->dp_typ = DOSPTYP_386BSD;
382                 partp->dp_flag = ACTIVE;
383                 partp->dp_start = dos_sectors;
384                 partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs -
385                     dos_sectors;
386                 dos(partp);
387                 if (v_flag)
388                         print_s0();
389                 if (!t_flag)
390                         write_s0();
391                 exit(0);
392         }
393         if (f_flag) {
394             if (read_s0() || i_flag)
395                 reset_boot();
396             if (!read_config(f_flag))
397                 exit(1);
398             if (v_flag)
399                 print_s0();
400             if (!t_flag)
401                 write_s0();
402         } else {
403             if(u_flag)
404                 get_params_to_use();
405             else
406                 print_params();
407
408             if (read_s0())
409                 init_sector0(dos_sectors);
410
411             printf("Media sector size is %d\n", secsize);
412             printf("Warning: BIOS sector numbering starts with sector 1\n");
413             printf("Information from DOS bootblock is:\n");
414             if (partition == -1)
415                 for (i = 1; i <= NDOSPART; i++)
416                     change_part(i);
417             else
418                 change_part(partition);
419
420             if (u_flag || a_flag)
421                 change_active(partition);
422
423             if (B_flag)
424                 change_code();
425
426             if (u_flag || a_flag || B_flag) {
427                 if (!t_flag) {
428                     printf("\nWe haven't changed the partition table yet.  ");
429                     printf("This is your last chance.\n");
430                 }
431                 print_s0();
432                 if (!t_flag) {
433                     if (ok("Should we write new partition table?"))
434                         write_s0();
435                 } else {
436                     printf("\n-t flag specified -- partition table not written.\n");
437                 }
438             }
439         }
440
441         exit(0);
442 }
443
444 static void
445 usage()
446 {
447         fprintf(stderr, "%s%s",
448                 "usage: fdisk [-BIaipqstu] [-b bootcode] [-1234] [disk]\n",
449                 "       fdisk -f configfile [-itv] [disk]\n");
450         exit(1);
451 }
452
453 static void
454 print_s0(void)
455 {
456         int     i;
457
458         print_params();
459         printf("Information from DOS bootblock is:\n");
460         for (i = 1; i <= NDOSPART; i++) {
461                 printf("%d: ", i);
462                 print_part(&mboot.parts[i - 1]);
463         }
464 }
465
466 static struct dos_partition mtpart;
467
468 static void
469 print_part(const struct dos_partition *partp)
470 {
471         u_int64_t part_mb;
472
473         if (!bcmp(partp, &mtpart, sizeof (struct dos_partition))) {
474                 printf("<UNUSED>\n");
475                 return;
476         }
477         /*
478          * Be careful not to overflow.
479          */
480         part_mb = partp->dp_size;
481         part_mb *= secsize;
482         part_mb /= (1024 * 1024);
483         printf("sysid %d (%#04x),(%s)\n", partp->dp_typ, partp->dp_typ,
484             get_type(partp->dp_typ));
485         printf("    start %lu, size %lu (%ju Meg), flag %x%s\n",
486                 (u_long)partp->dp_start,
487                 (u_long)partp->dp_size, 
488                 (uintmax_t)part_mb,
489                 partp->dp_flag,
490                 partp->dp_flag == ACTIVE ? " (active)" : "");
491         printf("\tbeg: cyl %d/ head %d/ sector %d;\n\tend: cyl %d/ head %d/ sector %d\n"
492                 ,DPCYL(partp->dp_scyl, partp->dp_ssect)
493                 ,partp->dp_shd
494                 ,DPSECT(partp->dp_ssect)
495                 ,DPCYL(partp->dp_ecyl, partp->dp_esect)
496                 ,partp->dp_ehd
497                 ,DPSECT(partp->dp_esect));
498 }
499
500
501 static void
502 init_boot(void)
503 {
504 #ifndef __ia64__
505         const char *fname;
506         int fdesc, n;
507         struct stat sb;
508
509         fname = b_flag ? b_flag : "/boot/mbr";
510         if ((fdesc = open(fname, O_RDONLY)) == -1 ||
511             fstat(fdesc, &sb) == -1)
512                 err(1, "%s", fname);
513         if ((mboot.bootinst_size = sb.st_size) % secsize != 0)
514                 errx(1, "%s: length must be a multiple of sector size", fname);
515         if (mboot.bootinst != NULL)
516                 free(mboot.bootinst);
517         if ((mboot.bootinst = malloc(mboot.bootinst_size = sb.st_size)) == NULL)
518                 errx(1, "%s: unable to allocate read buffer", fname);
519         if ((n = read(fdesc, mboot.bootinst, mboot.bootinst_size)) == -1 ||
520             close(fdesc))
521                 err(1, "%s", fname);
522         if (n != mboot.bootinst_size)
523                 errx(1, "%s: short read", fname);
524 #else
525         if (mboot.bootinst != NULL)
526                 free(mboot.bootinst);
527         mboot.bootinst_size = secsize;
528         if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL)
529                 errx(1, "unable to allocate boot block buffer");
530         memset(mboot.bootinst, 0, mboot.bootinst_size);
531         le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
532 #endif
533 }
534
535
536 static void
537 init_sector0(unsigned long start)
538 {
539         struct dos_partition *partp = &mboot.parts[0];
540
541         init_boot();
542
543         partp->dp_typ = DOSPTYP_386BSD;
544         partp->dp_flag = ACTIVE;
545         start = ((start + dos_sectors - 1) / dos_sectors) * dos_sectors;
546         if(start == 0)
547                 start = dos_sectors;
548         partp->dp_start = start;
549         partp->dp_size = (disksecs / dos_cylsecs) * dos_cylsecs - start;
550
551         dos(partp);
552 }
553
554 static void
555 change_part(int i)
556 {
557     struct dos_partition *partp = &mboot.parts[i - 1];
558
559     printf("The data for partition %d is:\n", i);
560     print_part(partp);
561
562     if (u_flag && ok("Do you want to change it?")) {
563         int tmp;
564
565         if (i_flag) {
566             bzero(partp, sizeof (*partp));
567             if (i == 1) {
568                 init_sector0(1);
569                 printf("\nThe static data for the slice 1 has been reinitialized to:\n");
570                 print_part(partp);
571             }
572         }
573
574         do {
575                 Decimal("sysid (165=FreeBSD)", partp->dp_typ, tmp);
576                 Decimal("start", partp->dp_start, tmp);
577                 Decimal("size", partp->dp_size, tmp);
578                 if (!sanitize_partition(partp)) {
579                         warnx("ERROR: failed to adjust; setting sysid to 0");
580                         partp->dp_typ = 0;
581                 }
582
583                 if (ok("Explicitly specify beg/end address ?"))
584                 {
585                         int     tsec,tcyl,thd;
586                         tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
587                         thd = partp->dp_shd;
588                         tsec = DPSECT(partp->dp_ssect);
589                         Decimal("beginning cylinder", tcyl, tmp);
590                         Decimal("beginning head", thd, tmp);
591                         Decimal("beginning sector", tsec, tmp);
592                         partp->dp_scyl = DOSCYL(tcyl);
593                         partp->dp_ssect = DOSSECT(tsec,tcyl);
594                         partp->dp_shd = thd;
595
596                         tcyl = DPCYL(partp->dp_ecyl,partp->dp_esect);
597                         thd = partp->dp_ehd;
598                         tsec = DPSECT(partp->dp_esect);
599                         Decimal("ending cylinder", tcyl, tmp);
600                         Decimal("ending head", thd, tmp);
601                         Decimal("ending sector", tsec, tmp);
602                         partp->dp_ecyl = DOSCYL(tcyl);
603                         partp->dp_esect = DOSSECT(tsec,tcyl);
604                         partp->dp_ehd = thd;
605                 } else
606                         dos(partp);
607
608                 print_part(partp);
609         } while (!ok("Are we happy with this entry?"));
610     }
611 }
612
613 static void
614 print_params()
615 {
616         printf("parameters extracted from in-core disklabel are:\n");
617         printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
618                         ,cyls,heads,sectors,cylsecs);
619         if (dos_cyls > 1023 || dos_heads > 255 || dos_sectors > 63)
620                 printf("Figures below won't work with BIOS for partitions not in cyl 1\n");
621         printf("parameters to be used for BIOS calculations are:\n");
622         printf("cylinders=%d heads=%d sectors/track=%d (%d blks/cyl)\n\n"
623                 ,dos_cyls,dos_heads,dos_sectors,dos_cylsecs);
624 }
625
626 static void
627 change_active(int which)
628 {
629         struct dos_partition *partp = &mboot.parts[0];
630         int active, i, new, tmp;
631
632         active = -1;
633         for (i = 0; i < NDOSPART; i++) {
634                 if ((partp[i].dp_flag & ACTIVE) == 0)
635                         continue;
636                 printf("Partition %d is marked active\n", i + 1);
637                 if (active == -1)
638                         active = i + 1;
639         }
640         if (a_flag && which != -1)
641                 active = which;
642         else if (active == -1)
643                 active = 1;
644
645         if (!ok("Do you want to change the active partition?"))
646                 return;
647 setactive:
648         do {
649                 new = active;
650                 Decimal("active partition", new, tmp);
651                 if (new < 1 || new > 4) {
652                         printf("Active partition number must be in range 1-4."
653                                         "  Try again.\n");
654                         goto setactive;
655                 }
656                 active = new;
657         } while (!ok("Are you happy with this choice"));
658         for (i = 0; i < NDOSPART; i++)
659                 partp[i].dp_flag = 0;
660         if (active > 0 && active <= NDOSPART)
661                 partp[active-1].dp_flag = ACTIVE;
662 }
663
664 static void
665 change_code()
666 {
667         if (ok("Do you want to change the boot code?"))
668                 init_boot();
669 }
670
671 void
672 get_params_to_use()
673 {
674         int     tmp;
675         print_params();
676         if (ok("Do you want to change our idea of what BIOS thinks ?"))
677         {
678                 do
679                 {
680                         Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
681                         Decimal("BIOS's idea of #heads", dos_heads, tmp);
682                         Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
683                         dos_cylsecs = dos_heads * dos_sectors;
684                         print_params();
685                 }
686                 while(!ok("Are you happy with this choice"));
687         }
688 }
689
690
691 /***********************************************\
692 * Change real numbers into strange dos numbers  *
693 \***********************************************/
694 static void
695 dos(struct dos_partition *partp)
696 {
697         int cy, sec;
698         u_int32_t end;
699
700         if (partp->dp_typ == 0 && partp->dp_start == 0 && partp->dp_size == 0) {
701                 memcpy(partp, &mtpart, sizeof(*partp));
702                 return;
703         }
704
705         /* Start c/h/s. */
706         partp->dp_shd = partp->dp_start % dos_cylsecs / dos_sectors;
707         cy = partp->dp_start / dos_cylsecs;
708         sec = partp->dp_start % dos_sectors + 1;
709         partp->dp_scyl = DOSCYL(cy);
710         partp->dp_ssect = DOSSECT(sec, cy);
711
712         /* End c/h/s. */
713         end = partp->dp_start + partp->dp_size - 1;
714         partp->dp_ehd = end % dos_cylsecs / dos_sectors;
715         cy = end / dos_cylsecs;
716         sec = end % dos_sectors + 1;
717         partp->dp_ecyl = DOSCYL(cy);
718         partp->dp_esect = DOSSECT(sec, cy);
719 }
720
721 static int
722 open_disk(int flag)
723 {
724         int rwmode;
725
726         /* Write mode if one of these flags are set. */
727         rwmode = (a_flag || I_flag || B_flag || flag);
728         fd = g_open(disk, rwmode);
729         /* If the mode fails, try read-only if we didn't. */
730         if (fd == -1 && errno == EPERM && rwmode)
731                 fd = g_open(disk, 0);
732         if (fd == -1 && errno == ENXIO)
733                 return -2;
734         if (fd == -1) {
735                 warnx("can't open device %s", disk);
736                 return -1;
737         }
738         if (get_params() == -1) {
739                 warnx("can't get disk parameters on %s", disk);
740                 return -1;
741         }
742         return fd;
743 }
744
745 static ssize_t
746 read_disk(off_t sector, void *buf)
747 {
748
749         lseek(fd, (sector * 512), 0);
750         if (secsize == 0)
751                 for (secsize = MIN_SEC_SIZE; secsize <= MAX_SEC_SIZE;
752                      secsize *= 2) {
753                         /* try the read */
754                         int size = read(fd, buf, secsize);
755                         if (size == secsize)
756                                 /* it worked so return */
757                                 return secsize;
758                 }
759         else
760                 return read(fd, buf, secsize);
761
762         /* we failed to read at any of the sizes */
763         return -1;
764 }
765
766 static int
767 write_disk(off_t sector, void *buf)
768 {
769         int error;
770         struct gctl_req *grq;
771         const char *errmsg;
772         char fbuf[BUFSIZ], *pname;
773         int i, fdw;
774
775         grq = gctl_get_handle();
776         gctl_ro_param(grq, "verb", -1, "write MBR");
777         gctl_ro_param(grq, "class", -1, "MBR");
778         pname = g_providername(fd);
779         if (pname == NULL) {
780                 warn("Error getting providername for %s", disk);
781                 return (-1);
782         }
783         gctl_ro_param(grq, "geom", -1, pname);
784         gctl_ro_param(grq, "data", secsize, buf);
785         errmsg = gctl_issue(grq);
786         free(pname);
787         if (errmsg == NULL) {
788                 gctl_free(grq);
789                 return(0);
790         }
791         if (!q_flag)    /* GEOM errors are benign, not all devices supported */
792                 warnx("%s", errmsg);
793         gctl_free(grq);
794
795         error = pwrite(fd, buf, secsize, (sector * 512));
796         if (error == secsize)
797                 return (0);
798
799         for (i = 1; i < 5; i++) {
800                 sprintf(fbuf, "%ss%d", disk, i);
801                 fdw = open(fbuf, O_RDWR, 0);
802                 if (fdw < 0)
803                         continue;
804                 error = ioctl(fdw, DIOCSMBR, buf);
805                 close(fdw);
806                 if (error == 0)
807                         return (0);
808         }
809         warnx("Failed to write sector zero");
810         return(EINVAL);
811 }
812
813 static int
814 get_params()
815 {
816         int error;
817         u_int u;
818         off_t o;
819
820         error = ioctl(fd, DIOCGFWSECTORS, &u);
821         if (error == 0)
822                 sectors = dos_sectors = u;
823         else
824                 sectors = dos_sectors = 63;
825
826         error = ioctl(fd, DIOCGFWHEADS, &u);
827         if (error == 0)
828                 heads = dos_heads = u;
829         else
830                 heads = dos_heads = 255;
831
832         dos_cylsecs = cylsecs = heads * sectors;
833         disksecs = cyls * heads * sectors;
834
835         u = g_sectorsize(fd);
836         if (u <= 0)
837                 return (-1);
838
839         o = g_mediasize(fd);
840         if (o < 0)
841                 return (-1);
842         disksecs = o / u;
843         cyls = dos_cyls = o / (u * dos_heads * dos_sectors);
844
845         return (disksecs);
846 }
847
848 static int
849 read_s0()
850 {
851         int i;
852
853         mboot.bootinst_size = secsize;
854         if (mboot.bootinst != NULL)
855                 free(mboot.bootinst);
856         if ((mboot.bootinst = malloc(mboot.bootinst_size)) == NULL) {
857                 warnx("unable to allocate buffer to read fdisk "
858                       "partition table");
859                 return -1;
860         }
861         if (read_disk(0, mboot.bootinst) == -1) {
862                 warnx("can't read fdisk partition table");
863                 return -1;
864         }
865         if (le16dec(&mboot.bootinst[DOSMAGICOFFSET]) != DOSMAGIC) {
866                 warnx("invalid fdisk partition table found");
867                 /* So should we initialize things */
868                 return -1;
869         }
870         for (i = 0; i < NDOSPART; i++)
871                 dos_partition_dec(
872                     &mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
873                     &mboot.parts[i]);
874         return 0;
875 }
876
877 static int
878 write_s0()
879 {
880         int     sector, i;
881
882         if (iotest) {
883                 print_s0();
884                 return 0;
885         }
886         for(i = 0; i < NDOSPART; i++)
887                 dos_partition_enc(&mboot.bootinst[DOSPARTOFF + i * DOSPARTSIZE],
888                     &mboot.parts[i]);
889         le16enc(&mboot.bootinst[DOSMAGICOFFSET], DOSMAGIC);
890         for(sector = 0; sector < mboot.bootinst_size / secsize; sector++) 
891                 if (write_disk(sector,
892                                &mboot.bootinst[sector * secsize]) == -1) {
893                         warn("can't write fdisk partition table");
894                         return -1;
895                 }
896         return(0);
897 }
898
899
900 static int
901 ok(const char *str)
902 {
903         printf("%s [n] ", str);
904         fflush(stdout);
905         if (fgets(lbuf, LBUF, stdin) == NULL)
906                 exit(1);
907         lbuf[strlen(lbuf)-1] = 0;
908
909         if (*lbuf &&
910                 (!strcmp(lbuf, "yes") || !strcmp(lbuf, "YES") ||
911                  !strcmp(lbuf, "y") || !strcmp(lbuf, "Y")))
912                 return 1;
913         else
914                 return 0;
915 }
916
917 static int
918 decimal(const char *str, int *num, int deflt)
919 {
920         int acc = 0, c;
921         char *cp;
922
923         while (1) {
924                 printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
925                 fflush(stdout);
926                 if (fgets(lbuf, LBUF, stdin) == NULL)
927                         exit(1);
928                 lbuf[strlen(lbuf)-1] = 0;
929
930                 if (!*lbuf)
931                         return 0;
932
933                 cp = lbuf;
934                 while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
935                 if (!c)
936                         return 0;
937                 while ((c = *cp++)) {
938                         if (c <= '9' && c >= '0')
939                                 acc = acc * 10 + c - '0';
940                         else
941                                 break;
942                 }
943                 if (c == ' ' || c == '\t')
944                         while ((c = *cp) && (c == ' ' || c == '\t')) cp++;
945                 if (!c) {
946                         *num = acc;
947                         return 1;
948                 } else
949                         printf("%s is an invalid decimal number.  Try again.\n",
950                                 lbuf);
951         }
952
953 }
954
955
956 static void
957 parse_config_line(char *line, CMD *command)
958 {
959     char        *cp, *end;
960
961     cp = line;
962     while (1) {
963         memset(command, 0, sizeof(*command));
964
965         while (isspace(*cp)) ++cp;
966         if (*cp == '\0' || *cp == '#')
967             break;
968         command->cmd = *cp++;
969
970         /*
971          * Parse args
972          */
973             while (1) {
974             while (isspace(*cp)) ++cp;
975             if (*cp == '\0')
976                 break;          /* eol */
977             if (*cp == '#')
978                 break;          /* found comment */
979             if (isalpha(*cp))
980                 command->args[command->n_args].argtype = *cp++;
981             end = NULL;
982             command->args[command->n_args].arg_val = strtol(cp, &end, 0);
983             if (cp == end || (!isspace(*end) && *end != '\0')) {
984                 char ch;
985                 end = cp;
986                 while (!isspace(*end) && *end != '\0') ++end;
987                 ch = *end; *end = '\0';
988                 command->args[command->n_args].arg_str = strdup(cp);
989                 *end = ch;
990             } else
991                 command->args[command->n_args].arg_str = NULL;
992             cp = end;
993             command->n_args++;
994         }
995         break;
996     }
997 }
998
999
1000 static int
1001 process_geometry(CMD *command)
1002 {
1003     int         status = 1, i;
1004
1005     while (1) {
1006         geom_processed = 1;
1007             if (part_processed) {
1008             warnx(
1009         "ERROR line %d: the geometry specification line must occur before\n\
1010     all partition specifications",
1011                     current_line_number);
1012             status = 0;
1013             break;
1014         }
1015             if (command->n_args != 3) {
1016             warnx("ERROR line %d: incorrect number of geometry args",
1017                     current_line_number);
1018             status = 0;
1019             break;
1020         }
1021             dos_cyls = 0;
1022             dos_heads = 0;
1023             dos_sectors = 0;
1024             for (i = 0; i < 3; ++i) {
1025                     switch (command->args[i].argtype) {
1026             case 'c':
1027                 dos_cyls = command->args[i].arg_val;
1028                 break;
1029             case 'h':
1030                 dos_heads = command->args[i].arg_val;
1031                 break;
1032             case 's':
1033                 dos_sectors = command->args[i].arg_val;
1034                 break;
1035             default:
1036                 warnx(
1037                 "ERROR line %d: unknown geometry arg type: '%c' (0x%02x)",
1038                         current_line_number, command->args[i].argtype,
1039                         command->args[i].argtype);
1040                 status = 0;
1041                 break;
1042             }
1043         }
1044         if (status == 0)
1045             break;
1046
1047         dos_cylsecs = dos_heads * dos_sectors;
1048
1049         /*
1050          * Do sanity checks on parameter values
1051          */
1052             if (dos_cyls == 0) {
1053             warnx("ERROR line %d: number of cylinders not specified",
1054                     current_line_number);
1055             status = 0;
1056         }
1057             if (dos_cyls > 1024) {
1058             warnx(
1059         "WARNING line %d: number of cylinders (%d) may be out-of-range\n\
1060     (must be within 1-1024 for normal BIOS operation, unless the entire disk\n\
1061     is dedicated to FreeBSD)",
1062                     current_line_number, dos_cyls);
1063         }
1064
1065             if (dos_heads == 0) {
1066             warnx("ERROR line %d: number of heads not specified",
1067                     current_line_number);
1068             status = 0;
1069             } else if (dos_heads > 256) {
1070             warnx("ERROR line %d: number of heads must be within (1-256)",
1071                     current_line_number);
1072             status = 0;
1073         }
1074
1075             if (dos_sectors == 0) {
1076             warnx("ERROR line %d: number of sectors not specified",
1077                     current_line_number);
1078             status = 0;
1079             } else if (dos_sectors > 63) {
1080             warnx("ERROR line %d: number of sectors must be within (1-63)",
1081                     current_line_number);
1082             status = 0;
1083         }
1084
1085         break;
1086     }
1087     return (status);
1088 }
1089
1090 static u_int32_t
1091 str2sectors(const char *str)
1092 {
1093         char *end;
1094         unsigned long val;
1095
1096         val = strtoul(str, &end, 0);
1097         if (str == end || *end == '\0') {
1098                 warnx("ERROR line %d: unexpected size: \'%s\'",
1099                     current_line_number, str);
1100                 return NOSECTORS;
1101         }
1102
1103         if (*end == 'K') 
1104                 val *= 1024UL / secsize;
1105         else if (*end == 'M')
1106                 val *= 1024UL * 1024UL / secsize;
1107         else if (*end == 'G')
1108                 val *= 1024UL * 1024UL * 1024UL / secsize;
1109         else {
1110                 warnx("ERROR line %d: unexpected modifier: %c "
1111                     "(not K/M/G)", current_line_number, *end);
1112                 return NOSECTORS;
1113         }
1114
1115         return val;
1116 }
1117
1118 static int
1119 process_partition(CMD *command)
1120 {
1121     int                         status = 0, partition;
1122     u_int32_t                   prev_head_boundary, prev_cyl_boundary;
1123     u_int32_t                   adj_size, max_end;
1124     struct dos_partition        *partp;
1125
1126         while (1) {
1127         part_processed = 1;
1128                 if (command->n_args != 4) {
1129             warnx("ERROR line %d: incorrect number of partition args",
1130                     current_line_number);
1131             break;
1132         }
1133         partition = command->args[0].arg_val;
1134                 if (partition < 1 || partition > 4) {
1135             warnx("ERROR line %d: invalid partition number %d",
1136                     current_line_number, partition);
1137             break;
1138         }
1139         partp = &mboot.parts[partition - 1];
1140         bzero(partp, sizeof (*partp));
1141         partp->dp_typ = command->args[1].arg_val;
1142         if (command->args[2].arg_str != NULL) {
1143                 if (strcmp(command->args[2].arg_str, "*") == 0) {
1144                         int i;
1145                         partp->dp_start = dos_sectors;
1146                         for (i = 1; i < partition; i++) {
1147                                 struct dos_partition *prev_partp;
1148                                 prev_partp = ((struct dos_partition *)
1149                                     &mboot.parts) + i - 1;
1150                                 if (prev_partp->dp_typ != 0)
1151                                         partp->dp_start = prev_partp->dp_start +
1152                                             prev_partp->dp_size;
1153                         }
1154                         if (partp->dp_start % dos_sectors != 0) {
1155                                 prev_head_boundary = partp->dp_start /
1156                                     dos_sectors * dos_sectors;
1157                                 partp->dp_start = prev_head_boundary +
1158                                     dos_sectors;
1159                         }
1160                 } else {
1161                         partp->dp_start = str2sectors(command->args[2].arg_str);
1162                         if (partp->dp_start == NOSECTORS)
1163                                 break;
1164                 }
1165         } else
1166                 partp->dp_start = command->args[2].arg_val;
1167
1168         if (command->args[3].arg_str != NULL) {
1169                 if (strcmp(command->args[3].arg_str, "*") == 0)
1170                         partp->dp_size = ((disksecs / dos_cylsecs) *
1171                             dos_cylsecs) - partp->dp_start;
1172                 else {
1173                         partp->dp_size = str2sectors(command->args[3].arg_str);
1174                         if (partp->dp_size == NOSECTORS)
1175                                 break;
1176                 }
1177                 prev_cyl_boundary = ((partp->dp_start + partp->dp_size) /
1178                     dos_cylsecs) * dos_cylsecs;
1179                 if (prev_cyl_boundary > partp->dp_start)
1180                         partp->dp_size = prev_cyl_boundary - partp->dp_start;
1181         } else
1182                 partp->dp_size = command->args[3].arg_val;
1183
1184         max_end = partp->dp_start + partp->dp_size;
1185
1186         if (partp->dp_typ == 0) {
1187             /*
1188              * Get out, the partition is marked as unused.
1189              */
1190             /*
1191              * Insure that it's unused.
1192              */
1193             bzero(partp, sizeof(*partp));
1194             status = 1;
1195             break;
1196         }
1197
1198         /*
1199          * Adjust start upwards, if necessary, to fall on a head boundary.
1200          */
1201                 if (partp->dp_start % dos_sectors != 0) {
1202             prev_head_boundary = partp->dp_start / dos_sectors * dos_sectors;
1203             if (max_end < dos_sectors ||
1204                             prev_head_boundary > max_end - dos_sectors) {
1205                 /*
1206                  * Can't go past end of partition
1207                  */
1208                 warnx(
1209         "ERROR line %d: unable to adjust start of partition %d to fall on\n\
1210     a head boundary",
1211                         current_line_number, partition);
1212                 break;
1213             }
1214             warnx(
1215         "WARNING: adjusting start offset of partition %d\n\
1216     from %u to %u, to fall on a head boundary",
1217                     partition, (u_int)partp->dp_start,
1218                     (u_int)(prev_head_boundary + dos_sectors));
1219             partp->dp_start = prev_head_boundary + dos_sectors;
1220         }
1221
1222         /*
1223          * Adjust size downwards, if necessary, to fall on a cylinder
1224          * boundary.
1225          */
1226         prev_cyl_boundary =
1227             ((partp->dp_start + partp->dp_size) / dos_cylsecs) * dos_cylsecs;
1228         if (prev_cyl_boundary > partp->dp_start)
1229             adj_size = prev_cyl_boundary - partp->dp_start;
1230                 else {
1231             warnx(
1232         "ERROR: could not adjust partition to start on a head boundary\n\
1233     and end on a cylinder boundary.");
1234             return (0);
1235         }
1236                 if (adj_size != partp->dp_size) {
1237             warnx(
1238         "WARNING: adjusting size of partition %d from %u to %u\n\
1239     to end on a cylinder boundary",
1240                     partition, (u_int)partp->dp_size, (u_int)adj_size);
1241             partp->dp_size = adj_size;
1242         }
1243                 if (partp->dp_size == 0) {
1244             warnx("ERROR line %d: size of partition %d is zero",
1245                     current_line_number, partition);
1246             break;
1247         }
1248
1249         dos(partp);
1250         status = 1;
1251         break;
1252     }
1253     return (status);
1254 }
1255
1256
1257 static int
1258 process_active(CMD *command)
1259 {
1260     int                         status = 0, partition, i;
1261     struct dos_partition        *partp;
1262
1263         while (1) {
1264         active_processed = 1;
1265                 if (command->n_args != 1) {
1266             warnx("ERROR line %d: incorrect number of active args",
1267                     current_line_number);
1268             status = 0;
1269             break;
1270         }
1271         partition = command->args[0].arg_val;
1272                 if (partition < 1 || partition > 4) {
1273             warnx("ERROR line %d: invalid partition number %d",
1274                     current_line_number, partition);
1275             break;
1276         }
1277         /*
1278          * Reset active partition
1279          */
1280         partp = mboot.parts;
1281         for (i = 0; i < NDOSPART; i++)
1282             partp[i].dp_flag = 0;
1283         partp[partition-1].dp_flag = ACTIVE;
1284
1285         status = 1;
1286         break;
1287     }
1288     return (status);
1289 }
1290
1291
1292 static int
1293 process_line(char *line)
1294 {
1295     CMD         command;
1296     int         status = 1;
1297
1298         while (1) {
1299         parse_config_line(line, &command);
1300                 switch (command.cmd) {
1301         case 0:
1302             /*
1303              * Comment or blank line
1304              */
1305             break;
1306         case 'g':
1307             /*
1308              * Set geometry
1309              */
1310             status = process_geometry(&command);
1311             break;
1312         case 'p':
1313             status = process_partition(&command);
1314             break;
1315         case 'a':
1316             status = process_active(&command);
1317             break;
1318         default:
1319             status = 0;
1320             break;
1321         }
1322         break;
1323     }
1324     return (status);
1325 }
1326
1327
1328 static int
1329 read_config(char *config_file)
1330 {
1331     FILE        *fp = NULL;
1332     int         status = 1;
1333     char        buf[1010];
1334
1335         while (1) {
1336                 if (strcmp(config_file, "-") != 0) {
1337             /*
1338              * We're not reading from stdin
1339              */
1340                         if ((fp = fopen(config_file, "r")) == NULL) {
1341                 status = 0;
1342                 break;
1343             }
1344                 } else {
1345             fp = stdin;
1346         }
1347         current_line_number = 0;
1348                 while (!feof(fp)) {
1349             if (fgets(buf, sizeof(buf), fp) == NULL)
1350                 break;
1351             ++current_line_number;
1352             status = process_line(buf);
1353             if (status == 0)
1354                 break;
1355             }
1356         break;
1357     }
1358         if (fp) {
1359         /*
1360          * It doesn't matter if we're reading from stdin, as we've reached EOF
1361          */
1362         fclose(fp);
1363     }
1364     return (status);
1365 }
1366
1367
1368 static void
1369 reset_boot(void)
1370 {
1371     int                         i;
1372     struct dos_partition        *partp;
1373
1374     init_boot();
1375     for (i = 0; i < 4; ++i) {
1376         partp = &mboot.parts[i];
1377         bzero(partp, sizeof(*partp));
1378     }
1379 }
1380
1381 static int
1382 sanitize_partition(struct dos_partition *partp)
1383 {
1384     u_int32_t                   prev_head_boundary, prev_cyl_boundary;
1385     u_int32_t                   max_end, size, start;
1386
1387     start = partp->dp_start;
1388     size = partp->dp_size;
1389     max_end = start + size;
1390     /* Only allow a zero size if the partition is being marked unused. */
1391     if (size == 0) {
1392         if (start == 0 && partp->dp_typ == 0)
1393             return (1);
1394         warnx("ERROR: size of partition is zero");
1395         return (0);
1396     }
1397     /* Return if no adjustment is necessary. */
1398     if (start % dos_sectors == 0 && (start + size) % dos_sectors == 0)
1399         return (1);
1400
1401     if (start == 0) {
1402             warnx("WARNING: partition overlaps with partition table");
1403             if (ok("Correct this automatically?"))
1404                     start = dos_sectors;
1405     }
1406     if (start % dos_sectors != 0)
1407         warnx("WARNING: partition does not start on a head boundary");
1408     if ((start  +size) % dos_sectors != 0)
1409         warnx("WARNING: partition does not end on a cylinder boundary");
1410     warnx("WARNING: this may confuse the BIOS or some operating systems");
1411     if (!ok("Correct this automatically?"))
1412         return (1);
1413
1414     /*
1415      * Adjust start upwards, if necessary, to fall on a head boundary.
1416      */
1417     if (start % dos_sectors != 0) {
1418         prev_head_boundary = start / dos_sectors * dos_sectors;
1419         if (max_end < dos_sectors ||
1420             prev_head_boundary >= max_end - dos_sectors) {
1421             /*
1422              * Can't go past end of partition
1423              */
1424             warnx(
1425     "ERROR: unable to adjust start of partition to fall on a head boundary");
1426             return (0);
1427         }
1428         start = prev_head_boundary + dos_sectors;
1429     }
1430
1431     /*
1432      * Adjust size downwards, if necessary, to fall on a cylinder
1433      * boundary.
1434      */
1435     prev_cyl_boundary = ((start + size) / dos_cylsecs) * dos_cylsecs;
1436     if (prev_cyl_boundary > start)
1437         size = prev_cyl_boundary - start;
1438     else {
1439         warnx("ERROR: could not adjust partition to start on a head boundary\n\
1440     and end on a cylinder boundary.");
1441         return (0);
1442     }
1443
1444     /* Finally, commit any changes to partp and return. */
1445     if (start != partp->dp_start) {
1446         warnx("WARNING: adjusting start offset of partition to %u",
1447             (u_int)start);
1448         partp->dp_start = start;
1449     }
1450     if (size != partp->dp_size) {
1451         warnx("WARNING: adjusting size of partition to %u", (u_int)size);
1452         partp->dp_size = size;
1453     }
1454
1455     return (1);
1456 }
1457
1458 /*
1459  * Try figuring out the root device's canonical disk name.
1460  * The following choices are considered:
1461  *   /dev/ad0s1a     => /dev/ad0
1462  *   /dev/da0a       => /dev/da0
1463  *   /dev/vinum/root => /dev/vinum/root
1464  */
1465 static char *
1466 get_rootdisk(void)
1467 {
1468         struct statfs rootfs;
1469         regex_t re;
1470 #define NMATCHES 2
1471         regmatch_t rm[NMATCHES];
1472         char *s;
1473         int rv;
1474
1475         if (statfs("/", &rootfs) == -1)
1476                 err(1, "statfs(\"/\")");
1477
1478         if ((rv = regcomp(&re, "^(/dev/[a-z/]+[0-9]+)([sp][0-9]+)?[a-h]?$",
1479                     REG_EXTENDED)) != 0)
1480                 errx(1, "regcomp() failed (%d)", rv);
1481         if ((rv = regexec(&re, rootfs.f_mntfromname, NMATCHES, rm, 0)) != 0)
1482                 errx(1,
1483 "mounted root fs resource doesn't match expectations (regexec returned %d)",
1484                     rv);
1485         if ((s = malloc(rm[1].rm_eo - rm[1].rm_so + 1)) == NULL)
1486                 errx(1, "out of memory");
1487         memcpy(s, rootfs.f_mntfromname + rm[1].rm_so,
1488             rm[1].rm_eo - rm[1].rm_so);
1489         s[rm[1].rm_eo - rm[1].rm_so] = 0;
1490
1491         return s;
1492 }