1 /* $NetBSD: bsddisklabel.c,v 1.52 2009/05/14 16:23:38 sborrill Exp $ */
4 * Copyright 1997 Piermont Information Systems Inc.
7 * Based on code written by Philip A. Nelson for Piermont Information
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed for the NetBSD Project by
21 * Piermont Information Systems Inc.
22 * 4. The name of Piermont Information Systems Inc. may not be used to endorse
23 * or promote products derived from this software without specific prior
26 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
36 * THE POSSIBILITY OF SUCH DAMAGE.
39 /* bsddisklabel.c -- generate standard BSD disklabel */
40 /* Included by appropriate arch/XXXX/md.c */
42 #include <sys/param.h>
43 #include <sys/sysctl.h>
45 #include <sys/utsname.h>
46 #include <sys/types.h>
48 #include <machine/cpu.h>
57 #include "menu_defs.h"
59 static int check_partitions(void);
61 /* For the current state of this file blame abs@NetBSD.org */
62 /* Even though he wasn't the last to hack it, but he did admit doing so :-) */
66 #define PART_TMP_RAMDISK -3
68 /* Defaults for things that might be defined in md.h */
70 #define PART_ROOT PART_A
73 #define PART_SWAP PART_B
76 #define PART_USR PART_ANY
83 #define DEFROOTSIZE 32
86 #define DEFUSRSIZE 128
89 #define DEFSWAPSIZE 128
93 save_ptn(int ptn
, daddr_t start
, daddr_t size
, int fstype
, const char *mountpt
)
100 maxptn
= getmaxpartitions();
102 if (ptn
< 0 || bsdlabel
[ptn
].pi_fstype
!= FS_UNUSED
) {
103 ptn
= getrawpartition() + 1;
104 #ifdef PART_FIRST_FREE
105 if (ptn
< PART_FIRST_FREE
)
106 ptn
= PART_FIRST_FREE
;
113 if (bsdlabel
[ptn
].pi_fstype
== FS_UNUSED
)
118 if (fstype
== FS_UNUSED
)
122 p
->pi_offset
= start
;
124 set_ptype(p
, fstype
, mountpt
? PIF_NEWFS
: 0);
126 if (mountpt
!= NULL
) {
127 for (pp
= 0; pp
< maxptn
; pp
++) {
128 if (strcmp(bsdlabel
[pp
].pi_mount
, mountpt
) == 0)
129 bsdlabel
[pp
].pi_flags
&= ~PIF_MOUNT
;
131 strlcpy(p
->pi_mount
, mountpt
, sizeof p
->pi_mount
);
132 p
->pi_flags
|= PIF_MOUNT
;
133 /* Default to logging, UFS2. */
134 if (p
->pi_fstype
== FS_BSDFFS
) {
135 p
->pi_flags
|= PIF_LOG
;
137 #ifndef HAVE_UFS2_BOOT
138 if (strcmp(mountpt
, "/") != 0)
140 p
->pi_flags
|= PIF_FFSv2
;
149 set_ptn_titles(menudesc
*m
, int opt
, void *arg
)
151 struct ptn_info
*pi
= arg
;
153 int sm
= MEG
/ sectorsize
;
157 p
= &pi
->ptn_sizes
[opt
];
158 if (p
->mount
[0] == 0) {
159 wprintw(m
->mw
, msg_string(MSG_add_another_ptn
));
163 if (p
== pi
->pool_part
)
164 snprintf(inc_free
, sizeof inc_free
, "(%" PRIi64
")",
165 (size
+ pi
->free_space
) / sm
);
168 wprintw(m
->mw
, "%6" PRIi64
"%8s%10" PRIi64
"%10" PRIi64
" %c %s",
169 size
/ sm
, inc_free
, size
/ dlcylsize
, size
,
170 p
== pi
->pool_part
? '+' : ' ', p
->mount
);
174 set_ptn_menu(struct ptn_info
*pi
)
179 for (m
= pi
->ptn_menus
, p
= pi
->ptn_sizes
;; m
++, p
++) {
181 m
->opt_menu
= OPT_NOMENU
;
183 m
->opt_action
= set_ptn_size
;
184 if (p
->mount
[0] == 0)
187 if (pi
->free_parts
!= 0)
189 m
->opt_name
= MSG_askunits
;
190 m
->opt_menu
= MENU_sizechoice
;
191 m
->opt_flags
= OPT_SUB
;
192 m
->opt_action
= NULL
;
195 if (pi
->free_space
>= 0)
196 snprintf(pi
->exit_msg
, sizeof pi
->exit_msg
,
197 msg_string(MSG_fssizesok
),
198 (int)(pi
->free_space
/ sizemult
), multname
, pi
->free_parts
);
200 snprintf(pi
->exit_msg
, sizeof pi
->exit_msg
,
201 msg_string(MSG_fssizesbad
),
202 (int)(-pi
->free_space
/ sizemult
), multname
, (uint
) -pi
->free_space
);
204 set_menu_numopts(pi
->menu_no
, m
- pi
->ptn_menus
);
208 set_ptn_size(menudesc
*m
, void *arg
)
210 struct ptn_info
*pi
= arg
;
215 daddr_t size
, old_size
;
218 p
= pi
->ptn_sizes
+ m
->cursel
;
220 if (pi
->free_parts
== 0 && p
->size
== 0)
221 /* Don't allow 'free_parts' to go negative */
224 if (p
->mount
[0] == 0) {
225 msg_prompt_win(MSG_askfsmount
, -1, 18, 0, 0,
226 NULL
, p
->mount
, sizeof p
->mount
);
227 if (p
->mount
[0] == 0)
236 snprintf(dflt
, sizeof dflt
, "%" PRIi64
"%s",
237 size
, p
== pi
->pool_part
? "+" : "");
241 msg_prompt_win(MSG_askfssize
, -1, 18, 0, 0,
242 dflt
, answer
, sizeof answer
,
244 /* Some special cases when /usr is first given a size */
245 if (old_size
== 0 && !strcmp(p
->mount
, "/usr")) {
246 /* Remove space for /usr from / */
247 if (!pi
->ptn_sizes
[0].changed
) {
248 pi
->ptn_sizes
[0].size
-= p
->dflt_size
;
249 pi
->free_space
+= p
->dflt_size
;
250 pi
->ptn_sizes
[0].changed
= 1;
252 /* hack to add free space to default sized /usr */
253 if (!strcmp(answer
, dflt
)) {
259 size
= strtoul(answer
, &cp
, 0);
271 mult
= MEG
/ sectorsize
;
275 mult
= 1024 * MEG
/ sectorsize
;
288 if (*cp
== 0 || *cp
== '+')
292 size
= NUMSEC(size
, mult
, dlcylsize
);
293 if (p
->ptn_id
== PART_TMP_RAMDISK
) {
297 if (p
== pi
->pool_part
)
298 pi
->pool_part
= NULL
;
299 if (*cp
== '+' && p
->limit
== 0) {
304 if (p
->limit
!= 0 && size
> p
->limit
)
307 if (size
!= old_size
)
309 pi
->free_space
+= old_size
- size
;
314 if (p
->ptn_id
== PART_EXTRA
)
316 (char *)&pi
->ptn_sizes
[MAXPARTITIONS
]
319 int f
= pi
->free_space
;
322 if (f
< mult
&& -f
< mult
) {
324 * Round size to end of available space,
325 * but keep cylinder alignment
328 f
= -roundup(-f
, dlcylsize
);
330 f
= rounddown(f
, dlcylsize
);
345 get_ptn_sizes(daddr_t part_start
, daddr_t sectors
, int no_swap
)
348 int maxpart
= getmaxpartitions();
349 int sm
; /* sectors in 1MB */
353 static struct ptn_info pi
= { -1, {
355 { PART_ROOT
, { '/', '\0' },
356 DEFROOTSIZE
, DEFROOTSIZE
, 0, 0},
358 { PART_SWAP
, { 's', 'w', 'a', 'p', '\0' },
359 DEFSWAPSIZE
, DEFSWAPSIZE
, 0, 0 },
362 { 't', 'm', 'p', ' ', '(', 't', 'm', 'p', 'f', 's', ')', '\0' },
364 { 't', 'm', 'p', ' ', '(', 'm', 'f', 's', ')', '\0' },
368 { PART_USR
, { '/', 'u', 's', 'r', '\0' }, DEFUSRSIZE
,
370 { PART_ANY
, { '/', 'v', 'a', 'r', '\0' }, DEFVARSIZE
,
372 { PART_ANY
, { '/', 'h', 'o', 'm', 'e', '\0' }, 0,
375 { NULL
, OPT_NOMENU
, 0, set_ptn_size
},
376 { MSG_askunits
, MENU_sizechoice
, OPT_SUB
, NULL
},
377 }, 0, 0, NULL
, { 0 } };
379 if (maxpart
> MAXPARTITIONS
)
380 maxpart
= MAXPARTITIONS
; /* sanity */
382 msg_display(MSG_ptnsizes
);
383 msg_table_add(MSG_ptnheaders
);
385 if (pi
.menu_no
< 0) {
386 /* If there is a swap partition elsewhere, don't add one here.*/
388 pi
.ptn_sizes
[PI_SWAP
].size
= 0;
390 #if DEFSWAPSIZE == -1
391 /* Dynamic swap size. */
392 pi
.ptn_sizes
[PI_SWAP
].dflt_size
= get_ramsize();
393 pi
.ptn_sizes
[PI_SWAP
].size
=
394 pi
.ptn_sizes
[PI_SWAP
].dflt_size
;
398 /* If installing X increase default size of /usr */
399 if (set_X11_selected())
400 pi
.ptn_sizes
[PI_USR
].dflt_size
+= XNEEDMB
;
402 /* Start of planning to give free space to / */
403 pi
.pool_part
= &pi
.ptn_sizes
[PI_ROOT
];
404 /* Make size of root include default size of /usr */
405 pi
.ptn_sizes
[PI_ROOT
].size
+= pi
.ptn_sizes
[PI_USR
].dflt_size
;
407 sm
= MEG
/ sectorsize
;
409 if (root_limit
!= 0) {
410 /* Bah - bios can not read all the disk, limit root */
411 pi
.ptn_sizes
[PI_ROOT
].limit
= root_limit
- part_start
;
412 /* Allocate a /usr partition if bios can't read
413 * everything except swap.
415 if (pi
.ptn_sizes
[PI_ROOT
].limit
416 < sectors
- pi
.ptn_sizes
[PI_SWAP
].size
* sm
) {
417 /* Root won't be able to access all the space */
418 /* Claw back space for /usr */
419 pi
.ptn_sizes
[PI_USR
].size
=
420 pi
.ptn_sizes
[PI_USR
].dflt_size
;
421 pi
.ptn_sizes
[PI_ROOT
].size
-=
422 pi
.ptn_sizes
[PI_USR
].dflt_size
;
423 pi
.ptn_sizes
[PI_ROOT
].changed
= 1;
424 /* Give free space to /usr */
425 pi
.pool_part
= &pi
.ptn_sizes
[PI_USR
];
429 /* Change preset sizes from MB to sectors */
430 pi
.free_space
= sectors
;
431 for (p
= pi
.ptn_sizes
; p
->mount
[0]; p
++) {
432 p
->size
= NUMSEC(p
->size
, sm
, dlcylsize
);
433 p
->dflt_size
= NUMSEC(p
->dflt_size
, sm
, dlcylsize
);
434 pi
.free_space
-= p
->size
;
437 /* Steal space from swap to make things fit.. */
438 if (pi
.free_space
< 0) {
439 i
= roundup(-pi
.free_space
, dlcylsize
);
440 if (i
> pi
.ptn_sizes
[PI_SWAP
].size
)
441 i
= pi
.ptn_sizes
[PI_SWAP
].size
;
442 pi
.ptn_sizes
[PI_SWAP
].size
-= i
;
446 /* Add space for 2 system dumps to / (traditional) */
447 i
= get_ramsize() * sm
;
448 i
= roundup(i
, dlcylsize
);
449 if (pi
.free_space
> i
* 2)
451 if (pi
.free_space
> i
) {
452 pi
.ptn_sizes
[PI_ROOT
].size
+= i
;
456 /* Ensure all of / is readable by the system boot code */
457 i
= pi
.ptn_sizes
[PI_ROOT
].limit
;
458 if (i
!= 0 && (i
-= pi
.ptn_sizes
[PI_ROOT
].size
) < 0) {
459 pi
.ptn_sizes
[PI_ROOT
].size
+= i
;
463 /* Count free partition slots */
465 for (i
= 0; i
< maxpart
; i
++) {
466 if (bsdlabel
[i
].pi_size
== 0)
469 for (i
= 0; i
< MAXPARTITIONS
; i
++) {
470 p
= &pi
.ptn_sizes
[i
];
471 if (i
!= 0 && p
->ptn_id
== 0)
472 p
->ptn_id
= PART_EXTRA
;
477 pi
.menu_no
= new_menu(0, pi
.ptn_menus
, nelem(pi
.ptn_menus
),
480 MC_ALWAYS_SCROLL
| MC_NOBOX
| MC_NOCLEAR
,
481 NULL
, set_ptn_titles
, NULL
,
482 "help", pi
.exit_msg
);
490 current_cylsize
= dlcylsize
;
491 process_menu(pi
.menu_no
, &pi
);
492 } while (pi
.free_space
< 0 || pi
.free_parts
< 0);
494 /* Give any cylinder fragment to last partition */
495 if (pi
.pool_part
!= NULL
|| pi
.free_space
< dlcylsize
) {
496 for (p
= pi
.ptn_sizes
+ nelem(pi
.ptn_sizes
) - 1; ;p
--) {
498 if (p
== pi
.ptn_sizes
)
502 if (p
->ptn_id
== PART_TMP_RAMDISK
)
504 p
->size
+= pi
.free_space
% dlcylsize
;
509 for (p
= pi
.ptn_sizes
; p
->mount
[0]; p
++, part_start
+= size
) {
511 if (p
== pi
.pool_part
) {
512 size
+= rounddown(pi
.free_space
, dlcylsize
);
513 if (p
->limit
!= 0 && size
> p
->limit
)
517 if (i
== PART_TMP_RAMDISK
) {
518 tmp_ramdisk_size
= size
;
524 if (i
== PART_SWAP
) {
525 save_ptn(i
, part_start
, size
, FS_SWAP
, NULL
);
528 save_ptn(i
, part_start
, size
, FS_BSDFFS
, p
->mount
);
533 * md back-end code for menu-driven BSD disklabel editor.
536 make_bsd_partitions(void)
540 int maxpart
= getmaxpartitions();
542 int part_raw
, part_bsd
;
544 int no_swap
= 0, valid_part
= -1;
548 * Initialize global variables that track space used on this disk.
549 * Standard 4.4BSD 8-partition labels always cover whole disk.
552 ptsize
= dlsize
- ptstart
;
554 dlsize
= ptstart
+ ptsize
;
557 ptend
= ptstart
+ ptsize
;
559 /* Ask for layout type -- standard or special */
560 msg_display(MSG_layout
,
561 (int) (ptsize
/ (MEG
/ sectorsize
)),
562 DEFROOTSIZE
+ DEFSWAPSIZE
+ DEFUSRSIZE
,
563 DEFROOTSIZE
+ DEFSWAPSIZE
+ DEFUSRSIZE
+ XNEEDMB
);
565 process_menu(MENU_layout
, NULL
);
567 /* Set so we use the 'real' geometry for rounding, input in MB */
568 current_cylsize
= dlcylsize
;
569 set_sizemultname_meg();
571 /* Build standard partitions */
572 memset(&bsdlabel
, 0, sizeof bsdlabel
);
574 /* Set initial partition types to unused */
575 for (part
= 0 ; part
< maxpart
; ++part
)
576 bsdlabel
[part
].pi_fstype
= FS_UNUSED
;
578 /* Whole disk partition */
579 part_raw
= getrawpartition();
581 part_raw
= PART_C
; /* for sanity... */
582 bsdlabel
[part_raw
].pi_offset
= 0;
583 bsdlabel
[part_raw
].pi_size
= dlsize
;
585 if (part_raw
== PART_D
) {
586 /* Probably a system that expects an i386 style mbr */
588 bsdlabel
[PART_C
].pi_offset
= ptstart
;
589 bsdlabel
[PART_C
].pi_size
= ptsize
;
594 #if defined(PART_BOOT) && defined(BOOT_SIZE)
597 /* Treat big numbers as a byte count */
598 i
= (i
+ dlcylsize
* sectorsize
- 1) / (dlcylsize
* sectorsize
);
601 bsdlabel
[PART_BOOT
].pi_fstype
= FS_BOOT
;
602 bsdlabel
[PART_BOOT
].pi_size
= i
;
604 bsdlabel
[PART_BOOT
].pi_offset
= ptend
- i
;
607 bsdlabel
[PART_BOOT
].pi_offset
= ptstart
;
610 #elif defined(PART_BOOT)
612 bsdlabel
[PART_BOOT
].pi_fstype
= FS_BOOT
;
613 bsdlabel
[PART_BOOT
].pi_size
= bootsize
;
614 bsdlabel
[PART_BOOT
].pi_offset
= bootstart
;
615 #if defined(PART_BOOT_PI_FLAGS)
616 bsdlabel
[PART_BOOT
].pi_flags
|= PART_BOOT_PI_FLAGS
;
618 #if defined(PART_BOOT_PI_MOUNT)
619 strlcpy(bsdlabel
[PART_BOOT
].pi_mount
, PART_BOOT_PI_MOUNT
,
620 sizeof bsdlabel
[PART_BOOT
].pi_mount
);
623 #endif /* PART_BOOT w/o BOOT_SIZE */
625 #if defined(PART_SYSVBFS) && defined(SYSVBFS_SIZE)
626 bsdlabel
[PART_SYSVBFS
].pi_offset
= partstart
;
627 bsdlabel
[PART_SYSVBFS
].pi_fstype
= FS_SYSVBFS
;
628 bsdlabel
[PART_SYSVBFS
].pi_size
= SYSVBFS_SIZE
;
629 bsdlabel
[PART_SYSVBFS
].pi_flags
|= PIF_NEWFS
| PIF_MOUNT
;
630 strlcpy(bsdlabel
[PART_SYSVBFS
].pi_mount
, "/stand",
631 sizeof bsdlabel
[PART_SYSVBFS
].pi_mount
);
632 partstart
+= SYSVBFS_SIZE
;
636 bsdlabel
[PART_REST
].pi_offset
= 0;
637 bsdlabel
[PART_REST
].pi_size
= ptstart
;
640 if (layoutkind
== 4) {
642 * If 'oldlabel' is a default label created by the kernel it
643 * will have exactly one valid partition besides raw_part
644 * which covers the whole disk - but might lie outside the
645 * mbr partition we (by now) have offset by a few sectors.
646 * Check for this and and fix ut up.
649 for (i
= 0; i
< maxpart
; i
++) {
652 if (oldlabel
[i
].pi_size
> 0 && PI_ISBSDFS(&oldlabel
[i
])) {
653 if (valid_part
>= 0) {
654 /* nope, not the default case */
661 if (valid_part
>= 0 && oldlabel
[valid_part
].pi_offset
< ptstart
) {
662 oldlabel
[valid_part
].pi_offset
= ptstart
;
663 oldlabel
[valid_part
].pi_size
-= ptstart
;
668 * Save any partitions that are outside the area we are
670 * In particular this saves details of the other MBR
671 * partitions on a multiboot i386 system.
673 for (i
= maxpart
; i
--;) {
674 if (bsdlabel
[i
].pi_size
!= 0)
675 /* Don't overwrite special partitions */
678 if (p
->pi_fstype
== FS_UNUSED
|| p
->pi_size
== 0)
680 if (layoutkind
== 4) {
682 p
->pi_flags
|= PIF_MOUNT
;
683 if (layoutkind
== 4 && i
== valid_part
) {
684 int fstype
= p
->pi_fstype
;
686 strcpy(p
->pi_mount
, "/");
687 set_ptype(p
, fstype
, PIF_NEWFS
);
691 if (p
->pi_offset
< ptstart
+ ptsize
&&
692 p
->pi_offset
+ p
->pi_size
> ptstart
)
693 /* Not outside area we are allocating */
695 if (p
->pi_fstype
== FS_SWAP
)
698 bsdlabel
[i
] = oldlabel
[i
];
702 get_ptn_sizes(partstart
, ptend
- partstart
, no_swap
);
705 * OK, we have a partition table. Give the user the chance to
706 * edit it and verify it's OK, or abort altogether.
709 if (edit_and_check_label(bsdlabel
, maxpart
, part_raw
, part_bsd
) == 0) {
710 msg_display(MSG_abort
);
713 if (check_partitions() == 0)
717 msg_prompt(MSG_packname
, bsddiskname
, bsddiskname
, sizeof bsddiskname
);
719 /* save label to disk for MI code to update. */
720 (void) savenewlabel(bsdlabel
, maxpart
);
722 /* Everything looks OK. */
727 * check that there is at least a / somewhere.
730 check_partitions(void)
732 #ifdef HAVE_BOOTXX_xFS
736 #ifndef HAVE_UFS2_BOOT
740 #ifdef HAVE_BOOTXX_xFS
741 /* check if we have boot code for the root partition type */
742 bootxx
= bootxx_name();
743 if (bootxx
!= NULL
) {
744 rv
= access(bootxx
, R_OK
);
747 if (bootxx
== NULL
|| rv
!= 0) {
748 process_menu(MENU_ok
, deconst(MSG_No_Bootcode
));
752 #ifndef HAVE_UFS2_BOOT
753 fstype
= bsdlabel
[rootpart
].pi_fstype
;
754 if (fstype
== FS_BSDFFS
&&
755 (bsdlabel
[rootpart
].pi_flags
& PIF_FFSv2
) != 0) {
756 process_menu(MENU_ok
, deconst(MSG_cannot_ufs2_root
));
761 return md_check_partitions();