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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * This file contains functions to implement the partition menu commands.
33 #include "partition.h"
34 #include "menu_partition.h"
35 #include "menu_command.h"
41 /* Function prototypes for ANSI C Compilers */
42 static void nspaces(int);
43 static int ndigits(uint64_t);
47 /* Function prototypes for non-ANSI C Compilers */
48 static void nspaces();
54 * This routine implements the 'a' command. It changes the 'a' partition.
65 * This routine implements the 'b' command. It changes the 'b' partition.
76 * This routine implements the 'c' command. It changes the 'c' partition.
87 * This routine implements the 'd' command. It changes the 'd' partition.
98 * This routine implements the 'e' command. It changes the 'e' partition.
109 * This routine implements the 'f' command. It changes the 'f' partition.
120 * This routine implements the 'g' command. It changes the 'g' partition.
131 * This routine implements the 'h' command. It changes the 'h' partition.
142 * This routine implements the 'i' command. It is valid only for EFI
143 * labeled disks. This can be used only in expert mode.
154 * This routine implements the 'j' command. It changes the 'j' partition.
163 #endif /* defined(i386) */
170 struct dk_gpt
*efi_label
= cur_parts
->etoc
;
172 if (cur_parts
->etoc
->efi_altern_lba
== 1 ||
173 (cur_parts
->etoc
->efi_altern_lba
>=
174 cur_parts
->etoc
->efi_last_lba
)) {
175 err_print("Warning: No expanded capacity is found.\n");
179 delta
= efi_label
->efi_last_lba
- efi_label
->efi_altern_lba
;
180 nparts
= efi_label
->efi_nparts
;
183 efi_label
->efi_parts
[nparts
- 1].p_start
+= delta
;
184 efi_label
->efi_last_u_lba
+= delta
;
185 efi_label
->efi_altern_lba
= cur_parts
->etoc
->efi_last_lba
;
188 fmt_print("The expanded capacity is added to the unallocated space.\n");
193 * This routine implements the 'select' command. It allows the user
194 * to make a pre-defined partition map the current map.
199 struct partition_info
*pptr
, *parts
;
201 int i
, index
, deflt
, *defltptr
= NULL
;
204 blkaddr_t cyl_offset
;
207 parts
= cur_dtype
->dtype_plist
;
209 * If there are no pre-defined maps for this disk type, it's
213 err_print("No defined partition tables.\n");
218 * Loop through the pre-defined maps and list them by name. If
219 * the current map is one of them, make it the default. If any
220 * the maps are unnamed, label them as such.
222 for (i
= 0, pptr
= parts
; pptr
!= NULL
; pptr
= pptr
->pinfo_next
) {
223 if (cur_parts
== pptr
) {
227 if (pptr
->pinfo_name
== NULL
)
228 fmt_print(" %d. unnamed\n", i
++);
230 fmt_print(" %d. %s\n", i
++, pptr
->pinfo_name
);
232 ioparam
.io_bounds
.lower
= 0;
233 ioparam
.io_bounds
.upper
= i
- 1;
235 * Ask which map should be made current.
237 index
= input(FIO_INT
, "Specify table (enter its number)", ':',
238 &ioparam
, defltptr
, DATA_INPUT
);
239 for (i
= 0, pptr
= parts
; i
< index
; i
++, pptr
= pptr
->pinfo_next
)
241 if (cur_label
== L_TYPE_EFI
) {
243 cur_disk
->disk_parts
= cur_parts
= pptr
;
250 * Adjust for the boot and alternate sectors partition - assuming that
251 * the alternate sectors partition physical location follows
252 * immediately the boot partition and partition sizes are
253 * expressed in multiple of cylinder size.
255 cyl_offset
= pptr
->pinfo_map
[I_PARTITION
].dkl_cylno
+ 1;
256 if (pptr
->pinfo_map
[J_PARTITION
].dkl_nblk
!= 0) {
257 cyl_offset
= pptr
->pinfo_map
[J_PARTITION
].dkl_cylno
+
258 ((pptr
->pinfo_map
[J_PARTITION
].dkl_nblk
+
259 (spc() - 1)) / spc());
261 #else /* !defined(i386) */
265 #endif /* defined(i386) */
268 * Before we blow the current map away, do some limits checking.
270 for (i
= 0; i
< NDKMAP
; i
++) {
273 if (i
== I_PARTITION
|| i
== J_PARTITION
|| i
== C_PARTITION
) {
275 } else if (pptr
->pinfo_map
[i
].dkl_nblk
== 0) {
277 * Always accept starting cyl 0 if the size is 0 also
281 b_cylno
= cyl_offset
;
283 #endif /* defined(i386) */
284 if (pptr
->pinfo_map
[i
].dkl_cylno
< b_cylno
||
285 pptr
->pinfo_map
[i
].dkl_cylno
> (ncyl
-1)) {
287 "partition %c: starting cylinder %d is out of range\n",
289 pptr
->pinfo_map
[i
].dkl_cylno
);
292 if (pptr
->pinfo_map
[i
].dkl_nblk
> ((ncyl
-
293 pptr
->pinfo_map
[i
].dkl_cylno
) * spc())) {
295 "partition %c: specified # of blocks, %u, "
298 pptr
->pinfo_map
[i
].dkl_nblk
);
303 * Lock out interrupts so the lists don't get mangled.
307 * If the old current map is unnamed, delete it.
309 if (cur_parts
!= NULL
&& cur_parts
!= pptr
&&
310 cur_parts
->pinfo_name
== NULL
)
311 delete_partition(cur_parts
);
313 * Make the selected map current.
315 cur_disk
->disk_parts
= cur_parts
= pptr
;
317 #if defined(_SUNOS_VTOC_16)
318 for (i
= 0; i
< NDKMAP
; i
++) {
319 cur_parts
->vtoc
.v_part
[i
].p_start
=
320 (blkaddr_t
)(cur_parts
->pinfo_map
[i
].dkl_cylno
*
322 cur_parts
->vtoc
.v_part
[i
].p_size
=
323 (blkaddr_t
)cur_parts
->pinfo_map
[i
].dkl_nblk
;
325 #endif /* defined(_SUNOS_VTOC_16) */
333 * This routine implements the 'name' command. It allows the user
334 * to name the current partition map. If the map was already named,
335 * the name is changed. Once a map is named, the values of the partitions
336 * cannot be changed. Attempts to change them will cause another map
345 * check if there exists a partition table for the disk.
347 if (cur_parts
== NULL
) {
348 err_print("Current Disk has no partition table.\n");
354 * Ask for the name. Note that the input routine will malloc
355 * space for the name since we are using the OSTR input type.
357 name
= (char *)(uintptr_t)input(FIO_OSTR
,
358 "Enter table name (remember quotes)",
359 ':', (u_ioparam_t
*)NULL
, (int *)NULL
, DATA_INPUT
);
361 * Lock out interrupts.
365 * If it was already named, destroy the old name.
367 if (cur_parts
->pinfo_name
!= NULL
)
368 destroy_data(cur_parts
->pinfo_name
);
372 cur_parts
->pinfo_name
= name
;
380 * This routine implements the 'print' command. It lists the values
381 * for all the partitions in the current partition map.
387 * check if there exists a partition table for the disk.
389 if (cur_parts
== NULL
) {
390 err_print("Current Disk has no partition table.\n");
395 * Print the volume name, if it appears to be set
397 if (chk_volname(cur_disk
)) {
398 fmt_print("Volume: ");
399 print_volname(cur_disk
);
403 * Print the name of the current map.
405 if ((cur_parts
->pinfo_name
!= NULL
) && (cur_label
== L_TYPE_SOLARIS
)) {
406 fmt_print("Current partition table (%s):\n",
407 cur_parts
->pinfo_name
);
409 "Total disk cylinders available: %d + %d (reserved cylinders)\n\n", ncyl
, acyl
);
410 } else if ((cur_label
== L_TYPE_EFI
) && (cur_parts
->pinfo_name
!=
412 fmt_print("Current partition table (%s):\n",
413 cur_parts
->pinfo_name
);
415 "Total disk sectors available: %llu + %d (reserved sectors)\n\n",
416 cur_parts
->etoc
->efi_last_u_lba
- EFI_MIN_RESV_SIZE
,
418 } else if (cur_label
== L_TYPE_SOLARIS
) {
419 fmt_print("Current partition table (unnamed):\n");
421 "Total disk cylinders available: %d + %d (reserved cylinders)\n\n", ncyl
, acyl
);
422 } else if (cur_label
== L_TYPE_EFI
) {
423 fmt_print("Current partition table (unnamed):\n");
425 "Total disk sectors available: %llu + %d (reserved sectors)\n\n",
426 cur_parts
->etoc
->efi_last_u_lba
- EFI_MIN_RESV_SIZE
,
432 * Print the partition map itself
434 print_map(cur_parts
);
440 * Print a partition map
443 print_map(struct partition_info
*map
)
447 struct dk_gpt
*vtoc64
;
449 if (cur_label
== L_TYPE_EFI
) {
452 for (i
= 0; i
< vtoc64
->efi_nparts
; i
++) {
454 * we want to print partitions above 7 in expert mode only
455 * or if the partition is reserved
457 if (i
>= 7 && !expert_mode
&&
458 ((int)vtoc64
->efi_parts
[i
].p_tag
!=
463 print_efi_partition(vtoc64
, i
, want_header
);
470 * Loop through each partition, printing the header
474 for (i
= 0; i
< NDKMAP
; i
++) {
478 print_partition(map
, i
, want_header
);
486 * Print out one line of partition information,
487 * with optional header for EFI type disks.
491 print_efi_partition(struct dk_gpt
*map
, int partnum
, int want_header
)
493 int ncyl2_digits
= 0;
498 ncyl2_digits
= ndigits(map
->efi_last_u_lba
);
501 fmt_print("Tag Flag ");
502 fmt_print("First Sector");
503 nspaces(ncyl2_digits
);
505 nspaces(ncyl2_digits
);
506 fmt_print("Last Sector\n");
509 fmt_print(" %d ", partnum
);
510 s
= find_string(ptag_choices
,
511 (int)map
->efi_parts
[partnum
].p_tag
);
512 if (s
== (char *)NULL
)
514 nspaces(10 - (int)strlen(s
));
517 s
= find_string(pflag_choices
,
518 (int)map
->efi_parts
[partnum
].p_flag
);
519 if (s
== (char *)NULL
)
521 nspaces(6 - (int)strlen(s
));
526 secsize
= map
->efi_parts
[partnum
].p_size
;
528 fmt_print("%16llu", map
->efi_parts
[partnum
].p_start
);
529 nspaces(ncyl2_digits
);
532 fmt_print("%16llu", map
->efi_parts
[partnum
].p_start
);
533 scaled
= bn2mb(secsize
);
534 nspaces(ncyl2_digits
- 5);
535 if (scaled
>= (float)1024.0 * 1024) {
536 fmt_print("%8.2fTB", scaled
/((float)1024.0 * 1024));
537 } else if (scaled
>= (float)1024.0) {
538 fmt_print("%8.2fGB", scaled
/(float)1024.0);
540 fmt_print("%8.2fMB", scaled
);
543 nspaces(ncyl2_digits
);
544 if ((map
->efi_parts
[partnum
].p_start
+secsize
- 1) ==
548 fmt_print(" %llu \n",
549 map
->efi_parts
[partnum
].p_start
+secsize
- 1);
554 * Print out one line of partition information,
555 * with optional header.
559 print_partition(struct partition_info
*pinfo
, int partnum
, int want_header
)
569 blkaddr_t maxnblks
= 0;
573 * To align things nicely, we need to know the maximum
574 * width of the number of cylinders field.
577 for (i
= 0; i
< NDKMAP
; i
++) {
578 nblks
= (uint_t
)pinfo
->pinfo_map
[i
].dkl_nblk
;
579 cyl1
= pinfo
->pinfo_map
[i
].dkl_cylno
;
580 cyl2
= cyl1
+ (nblks
/ spc()) - 1;
582 maxcyl2
= max(cyl2
, maxcyl2
);
583 maxnblks
= max(nblks
, maxnblks
);
587 * Get the number of digits required
589 ncyl2_digits
= ndigits(maxcyl2
);
592 * Print the header, if necessary
596 fmt_print("Tag Flag ");
597 fmt_print("Cylinders");
598 nspaces(ncyl2_digits
);
599 fmt_print(" Size Blocks\n");
603 * Print the partition information
605 nblks
= pinfo
->pinfo_map
[partnum
].dkl_nblk
;
606 cyl1
= pinfo
->pinfo_map
[partnum
].dkl_cylno
;
607 cyl2
= cyl1
+ (nblks
/ spc()) - 1;
609 fmt_print(" %x ", partnum
);
612 * Print the partition tag. If invalid, print -
614 s
= find_string(ptag_choices
,
615 (int)pinfo
->vtoc
.v_part
[partnum
].p_tag
);
616 if (s
== (char *)NULL
)
618 nspaces(10 - (int)strlen(s
));
622 * Print the partition flag. If invalid print -
624 s
= find_string(pflag_choices
,
625 (int)pinfo
->vtoc
.v_part
[partnum
].p_flag
);
626 if (s
== (char *)NULL
)
628 nspaces(6 - (int)strlen(s
));
634 fmt_print("%6d ", cyl1
);
635 nspaces(ncyl2_digits
);
638 fmt_print("%6d - ", cyl1
);
639 nspaces(ncyl2_digits
- ndigits(cyl2
));
640 fmt_print("%d ", cyl2
);
641 scaled
= bn2mb(nblks
);
642 if (scaled
> (float)1024.0 * 1024.0) {
643 fmt_print("%8.2fTB ",
644 scaled
/((float)1024.0 * 1024.0));
645 } else if (scaled
> (float)1024.0) {
646 fmt_print("%8.2fGB ", scaled
/(float)1024.0);
648 fmt_print("%8.2fMB ", scaled
);
652 pr_dblock(fmt_print
, nblks
);
655 nspaces(ndigits(maxnblks
/spc()) - ndigits(nblks
/spc()));
657 * Allocates size of the printf format string.
658 * ndigits(ndigits(maxblks)) gives the byte size of
659 * the printf width field for maxnblks.
661 len
= strlen(" %") + ndigits(ndigits(maxnblks
)) + strlen("d\n") + 1;
663 (void) snprintf(s
, len
, "%s%u%s", " %", ndigits(maxnblks
), "u\n");
670 * Return true if a disk has a volume name
674 struct disk_info
*disk
;
676 return (disk
->v_volume
[0] != 0);
681 * Print the volume name, if it appears to be set
685 struct disk_info
*disk
;
691 for (i
= 0; i
< LEN_DKL_VVOL
; i
++, p
++) {
700 * Print a number of spaces
711 * Return the number of digits required to print a number
725 return (i
== 0 ? 1 : i
);