4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
26 * This file contains functions to implement the partition menu commands.
32 #include "partition.h"
33 #include "menu_partition.h"
34 #include "menu_command.h"
40 /* Function prototypes for ANSI C Compilers */
41 static void nspaces(int);
42 static int ndigits(uint64_t);
46 /* Function prototypes for non-ANSI C Compilers */
47 static void nspaces();
53 * This routine implements the 'a' command. It changes the 'a' partition.
64 * This routine implements the 'b' command. It changes the 'b' partition.
75 * This routine implements the 'c' command. It changes the 'c' partition.
86 * This routine implements the 'd' command. It changes the 'd' partition.
97 * This routine implements the 'e' command. It changes the 'e' partition.
108 * This routine implements the 'f' command. It changes the 'f' partition.
119 * This routine implements the 'g' command. It changes the 'g' partition.
130 * This routine implements the 'h' command. It changes the 'h' partition.
141 * This routine implements the 'i' command. It is valid only for EFI
142 * labeled disks. This can be used only in expert mode.
153 * This routine implements the 'j' command. It changes the 'j' partition.
162 #endif /* defined(i386) */
169 struct dk_gpt
*efi_label
= cur_parts
->etoc
;
171 if (cur_parts
->etoc
->efi_altern_lba
== 1 ||
172 (cur_parts
->etoc
->efi_altern_lba
>=
173 cur_parts
->etoc
->efi_last_lba
)) {
174 err_print("Warning: No expanded capacity is found.\n");
178 delta
= efi_label
->efi_last_lba
- efi_label
->efi_altern_lba
;
179 nparts
= efi_label
->efi_nparts
;
182 efi_label
->efi_parts
[nparts
- 1].p_start
+= delta
;
183 efi_label
->efi_last_u_lba
+= delta
;
184 efi_label
->efi_altern_lba
= cur_parts
->etoc
->efi_last_lba
;
187 fmt_print("The expanded capacity is added to the unallocated space.\n");
192 * This routine implements the 'select' command. It allows the user
193 * to make a pre-defined partition map the current map.
198 struct partition_info
*pptr
, *parts
;
200 int i
, index
, deflt
, *defltptr
= NULL
;
203 blkaddr_t cyl_offset
;
206 parts
= cur_dtype
->dtype_plist
;
208 * If there are no pre-defined maps for this disk type, it's
212 err_print("No defined partition tables.\n");
217 * Loop through the pre-defined maps and list them by name. If
218 * the current map is one of them, make it the default. If any
219 * the maps are unnamed, label them as such.
221 for (i
= 0, pptr
= parts
; pptr
!= NULL
; pptr
= pptr
->pinfo_next
) {
222 if (cur_parts
== pptr
) {
226 if (pptr
->pinfo_name
== NULL
)
227 fmt_print(" %d. unnamed\n", i
++);
229 fmt_print(" %d. %s\n", i
++, pptr
->pinfo_name
);
231 ioparam
.io_bounds
.lower
= 0;
232 ioparam
.io_bounds
.upper
= i
- 1;
234 * Ask which map should be made current.
236 index
= input(FIO_INT
, "Specify table (enter its number)", ':',
237 &ioparam
, defltptr
, DATA_INPUT
);
238 for (i
= 0, pptr
= parts
; i
< index
; i
++, pptr
= pptr
->pinfo_next
)
240 if (cur_label
== L_TYPE_EFI
) {
242 cur_disk
->disk_parts
= cur_parts
= pptr
;
249 * Adjust for the boot and alternate sectors partition - assuming that
250 * the alternate sectors partition physical location follows
251 * immediately the boot partition and partition sizes are
252 * expressed in multiple of cylinder size.
254 cyl_offset
= pptr
->pinfo_map
[I_PARTITION
].dkl_cylno
+ 1;
255 if (pptr
->pinfo_map
[J_PARTITION
].dkl_nblk
!= 0) {
256 cyl_offset
= pptr
->pinfo_map
[J_PARTITION
].dkl_cylno
+
257 ((pptr
->pinfo_map
[J_PARTITION
].dkl_nblk
+
258 (spc() - 1)) / spc());
260 #else /* !defined(i386) */
264 #endif /* defined(i386) */
267 * Before we blow the current map away, do some limits checking.
269 for (i
= 0; i
< NDKMAP
; i
++) {
272 if (i
== I_PARTITION
|| i
== J_PARTITION
|| i
== C_PARTITION
) {
274 } else if (pptr
->pinfo_map
[i
].dkl_nblk
== 0) {
276 * Always accept starting cyl 0 if the size is 0 also
280 b_cylno
= cyl_offset
;
282 #endif /* defined(i386) */
283 if (pptr
->pinfo_map
[i
].dkl_cylno
< b_cylno
||
284 pptr
->pinfo_map
[i
].dkl_cylno
> (ncyl
-1)) {
286 "partition %c: starting cylinder %d is out of range\n",
288 pptr
->pinfo_map
[i
].dkl_cylno
);
291 if (pptr
->pinfo_map
[i
].dkl_nblk
> ((ncyl
-
292 pptr
->pinfo_map
[i
].dkl_cylno
) * spc())) {
294 "partition %c: specified # of blocks, %u, "
297 pptr
->pinfo_map
[i
].dkl_nblk
);
302 * Lock out interrupts so the lists don't get mangled.
306 * If the old current map is unnamed, delete it.
308 if (cur_parts
!= NULL
&& cur_parts
!= pptr
&&
309 cur_parts
->pinfo_name
== NULL
)
310 delete_partition(cur_parts
);
312 * Make the selected map current.
314 cur_disk
->disk_parts
= cur_parts
= pptr
;
316 #if defined(_SUNOS_VTOC_16)
317 for (i
= 0; i
< NDKMAP
; i
++) {
318 cur_parts
->vtoc
.v_part
[i
].p_start
=
319 (blkaddr_t
)(cur_parts
->pinfo_map
[i
].dkl_cylno
*
321 cur_parts
->vtoc
.v_part
[i
].p_size
=
322 (blkaddr_t
)cur_parts
->pinfo_map
[i
].dkl_nblk
;
324 #endif /* defined(_SUNOS_VTOC_16) */
332 * This routine implements the 'name' command. It allows the user
333 * to name the current partition map. If the map was already named,
334 * the name is changed. Once a map is named, the values of the partitions
335 * cannot be changed. Attempts to change them will cause another map
344 * check if there exists a partition table for the disk.
346 if (cur_parts
== NULL
) {
347 err_print("Current Disk has no partition table.\n");
353 * Ask for the name. Note that the input routine will malloc
354 * space for the name since we are using the OSTR input type.
356 name
= (char *)(uintptr_t)input(FIO_OSTR
,
357 "Enter table name (remember quotes)",
358 ':', (u_ioparam_t
*)NULL
, NULL
, DATA_INPUT
);
360 * Lock out interrupts.
364 * If it was already named, destroy the old name.
366 if (cur_parts
->pinfo_name
!= NULL
)
367 destroy_data(cur_parts
->pinfo_name
);
371 cur_parts
->pinfo_name
= name
;
379 * This routine implements the 'print' command. It lists the values
380 * for all the partitions in the current partition map.
386 * check if there exists a partition table for the disk.
388 if (cur_parts
== NULL
) {
389 err_print("Current Disk has no partition table.\n");
394 * Print the volume name, if it appears to be set
396 if (chk_volname(cur_disk
)) {
397 fmt_print("Volume: ");
398 print_volname(cur_disk
);
402 * Print the name of the current map.
404 if ((cur_parts
->pinfo_name
!= NULL
) && (cur_label
== L_TYPE_SOLARIS
)) {
405 fmt_print("Current partition table (%s):\n",
406 cur_parts
->pinfo_name
);
407 fmt_print("Total disk cylinders available: %d + %d "
408 "(reserved cylinders)\n\n", ncyl
, acyl
);
409 } else if (cur_label
== L_TYPE_SOLARIS
) {
410 fmt_print("Current partition table (unnamed):\n");
411 fmt_print("Total disk cylinders available: %d + %d "
412 "(reserved cylinders)\n\n", ncyl
, acyl
);
413 } else if (cur_label
== L_TYPE_EFI
) {
414 fmt_print("Current partition table (%s):\n",
415 cur_parts
->pinfo_name
!= NULL
?
416 cur_parts
->pinfo_name
: "unnamed");
417 fmt_print("Total disk sectors available: %llu + %d "
418 "(reserved sectors)\n\n",
419 cur_parts
->etoc
->efi_last_u_lba
- EFI_MIN_RESV_SIZE
-
420 cur_parts
->etoc
->efi_first_u_lba
+ 1, EFI_MIN_RESV_SIZE
);
425 * Print the partition map itself
427 print_map(cur_parts
);
433 * Print a partition map
436 print_map(struct partition_info
*map
)
440 struct dk_gpt
*vtoc64
;
442 if (cur_label
== L_TYPE_EFI
) {
445 for (i
= 0; i
< vtoc64
->efi_nparts
; i
++) {
447 * we want to print partitions above 7 in expert mode only
448 * or if the partition is reserved
450 if (i
>= 7 && !expert_mode
&&
451 ((int)vtoc64
->efi_parts
[i
].p_tag
!=
456 print_efi_partition(vtoc64
, i
, want_header
);
463 * Loop through each partition, printing the header
467 for (i
= 0; i
< NDKMAP
; i
++) {
471 print_partition(map
, i
, want_header
);
479 * Print out one line of partition information,
480 * with optional header for EFI type disks.
484 print_efi_partition(struct dk_gpt
*map
, int partnum
, int want_header
)
486 int ncyl2_digits
= 0;
491 ncyl2_digits
= ndigits(map
->efi_last_u_lba
);
494 fmt_print("Tag Flag ");
495 fmt_print("First Sector");
496 nspaces(ncyl2_digits
);
498 nspaces(ncyl2_digits
);
499 fmt_print("Last Sector\n");
502 fmt_print(" %d ", partnum
);
503 s
= find_string(ptag_choices
,
504 (int)map
->efi_parts
[partnum
].p_tag
);
507 nspaces(10 - (int)strlen(s
));
510 s
= find_string(pflag_choices
,
511 (int)map
->efi_parts
[partnum
].p_flag
);
514 nspaces(6 - (int)strlen(s
));
519 secsize
= map
->efi_parts
[partnum
].p_size
;
521 fmt_print("%16llu", map
->efi_parts
[partnum
].p_start
);
522 nspaces(ncyl2_digits
);
525 fmt_print("%16llu", map
->efi_parts
[partnum
].p_start
);
526 scaled
= bn2mb(secsize
);
527 nspaces(ncyl2_digits
- 5);
528 if (scaled
>= (float)1024.0 * 1024) {
529 fmt_print("%8.2fTB", scaled
/((float)1024.0 * 1024));
530 } else if (scaled
>= (float)1024.0) {
531 fmt_print("%8.2fGB", scaled
/(float)1024.0);
533 fmt_print("%8.2fMB", scaled
);
536 nspaces(ncyl2_digits
);
537 if ((map
->efi_parts
[partnum
].p_start
+secsize
- 1) ==
541 fmt_print(" %llu \n",
542 map
->efi_parts
[partnum
].p_start
+secsize
- 1);
547 * Print out one line of partition information,
548 * with optional header.
552 print_partition(struct partition_info
*pinfo
, int partnum
, int want_header
)
562 blkaddr_t maxnblks
= 0;
566 * To align things nicely, we need to know the maximum
567 * width of the number of cylinders field.
570 for (i
= 0; i
< NDKMAP
; i
++) {
571 nblks
= (uint_t
)pinfo
->pinfo_map
[i
].dkl_nblk
;
572 cyl1
= pinfo
->pinfo_map
[i
].dkl_cylno
;
573 cyl2
= cyl1
+ (nblks
/ spc()) - 1;
575 maxcyl2
= max(cyl2
, maxcyl2
);
576 maxnblks
= max(nblks
, maxnblks
);
580 * Get the number of digits required
582 ncyl2_digits
= ndigits(maxcyl2
);
585 * Print the header, if necessary
589 fmt_print("Tag Flag ");
590 fmt_print("Cylinders");
591 nspaces(ncyl2_digits
);
592 fmt_print(" Size Blocks\n");
596 * Print the partition information
598 nblks
= pinfo
->pinfo_map
[partnum
].dkl_nblk
;
599 cyl1
= pinfo
->pinfo_map
[partnum
].dkl_cylno
;
600 cyl2
= cyl1
+ (nblks
/ spc()) - 1;
602 fmt_print(" %x ", partnum
);
605 * Print the partition tag. If invalid, print -
607 s
= find_string(ptag_choices
,
608 (int)pinfo
->vtoc
.v_part
[partnum
].p_tag
);
611 nspaces(10 - (int)strlen(s
));
615 * Print the partition flag. If invalid print -
617 s
= find_string(pflag_choices
,
618 (int)pinfo
->vtoc
.v_part
[partnum
].p_flag
);
621 nspaces(6 - (int)strlen(s
));
627 fmt_print("%6d ", cyl1
);
628 nspaces(ncyl2_digits
);
631 fmt_print("%6d - ", cyl1
);
632 nspaces(ncyl2_digits
- ndigits(cyl2
));
633 fmt_print("%d ", cyl2
);
634 scaled
= bn2mb(nblks
);
635 if (scaled
> (float)1024.0 * 1024.0) {
636 fmt_print("%8.2fTB ",
637 scaled
/((float)1024.0 * 1024.0));
638 } else if (scaled
> (float)1024.0) {
639 fmt_print("%8.2fGB ", scaled
/(float)1024.0);
641 fmt_print("%8.2fMB ", scaled
);
645 pr_dblock(fmt_print
, nblks
);
648 nspaces(ndigits(maxnblks
/spc()) - ndigits(nblks
/spc()));
650 * Allocates size of the printf format string.
651 * ndigits(ndigits(maxblks)) gives the byte size of
652 * the printf width field for maxnblks.
654 len
= strlen(" %") + ndigits(ndigits(maxnblks
)) + strlen("d\n") + 1;
656 (void) snprintf(s
, len
, "%s%u%s", " %", ndigits(maxnblks
), "u\n");
663 * Return true if a disk has a volume name
667 struct disk_info
*disk
;
669 return (disk
->v_volume
[0] != 0);
674 * Print the volume name, if it appears to be set
678 struct disk_info
*disk
;
684 for (i
= 0; i
< LEN_DKL_VVOL
; i
++, p
++) {
693 * Print a number of spaces
704 * Return the number of digits required to print a number
718 return (i
== 0 ? 1 : i
);