6930152 6192139 (no reboot audit -- PSARC/2009/354) points out less than optimal...
[unleashed.git] / usr / src / cmd / format / menu_partition.c
blobdf69959537db8ca42058561cb3e5c03f2f34e633
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
29 #include "global.h"
30 #include <stdlib.h>
31 #include <string.h>
33 #include "partition.h"
34 #include "menu_partition.h"
35 #include "menu_command.h"
36 #include "misc.h"
37 #include "param.h"
39 #ifdef __STDC__
41 /* Function prototypes for ANSI C Compilers */
42 static void nspaces(int);
43 static int ndigits(uint64_t);
45 #else /* __STDC__ */
47 /* Function prototypes for non-ANSI C Compilers */
48 static void nspaces();
49 static int ndigits();
51 #endif /* __STDC__ */
54 * This routine implements the 'a' command. It changes the 'a' partition.
56 int
57 p_apart()
60 change_partition(0);
61 return (0);
65 * This routine implements the 'b' command. It changes the 'b' partition.
67 int
68 p_bpart()
71 change_partition(1);
72 return (0);
76 * This routine implements the 'c' command. It changes the 'c' partition.
78 int
79 p_cpart()
82 change_partition(2);
83 return (0);
87 * This routine implements the 'd' command. It changes the 'd' partition.
89 int
90 p_dpart()
93 change_partition(3);
94 return (0);
98 * This routine implements the 'e' command. It changes the 'e' partition.
101 p_epart()
104 change_partition(4);
105 return (0);
109 * This routine implements the 'f' command. It changes the 'f' partition.
112 p_fpart()
115 change_partition(5);
116 return (0);
120 * This routine implements the 'g' command. It changes the 'g' partition.
123 p_gpart()
126 change_partition(6);
127 return (0);
131 * This routine implements the 'h' command. It changes the 'h' partition.
134 p_hpart()
137 change_partition(7);
138 return (0);
142 * This routine implements the 'i' command. It is valid only for EFI
143 * labeled disks. This can be used only in expert mode.
146 p_ipart()
148 change_partition(8);
149 return (0);
152 #if defined(i386)
154 * This routine implements the 'j' command. It changes the 'j' partition.
157 p_jpart()
160 change_partition(9);
161 return (0);
163 #endif /* defined(i386) */
166 p_expand()
168 uint64_t delta;
169 uint_t nparts;
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");
176 return (0);
179 delta = efi_label->efi_last_lba - efi_label->efi_altern_lba;
180 nparts = efi_label->efi_nparts;
182 enter_critical();
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;
186 exit_critical();
188 fmt_print("The expanded capacity is added to the unallocated space.\n");
189 return (0);
193 * This routine implements the 'select' command. It allows the user
194 * to make a pre-defined partition map the current map.
197 p_select()
199 struct partition_info *pptr, *parts;
200 u_ioparam_t ioparam;
201 int i, index, deflt, *defltptr = NULL;
202 blkaddr_t b_cylno;
203 #if defined(i386)
204 blkaddr_t cyl_offset;
205 #endif
207 parts = cur_dtype->dtype_plist;
209 * If there are no pre-defined maps for this disk type, it's
210 * an error.
212 if (parts == NULL) {
213 err_print("No defined partition tables.\n");
214 return (-1);
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) {
224 deflt = i;
225 defltptr = &deflt;
227 if (pptr->pinfo_name == NULL)
228 fmt_print(" %d. unnamed\n", i++);
229 else
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) {
242 enter_critical();
243 cur_disk->disk_parts = cur_parts = pptr;
244 exit_critical();
245 fmt_print("\n");
246 return (0);
248 #if defined(i386)
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) */
263 b_cylno = 0;
265 #endif /* defined(i386) */
268 * Before we blow the current map away, do some limits checking.
270 for (i = 0; i < NDKMAP; i++) {
272 #if defined(i386)
273 if (i == I_PARTITION || i == J_PARTITION || i == C_PARTITION) {
274 b_cylno = 0;
275 } else if (pptr->pinfo_map[i].dkl_nblk == 0) {
277 * Always accept starting cyl 0 if the size is 0 also
279 b_cylno = 0;
280 } else {
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)) {
286 err_print(
287 "partition %c: starting cylinder %d is out of range\n",
288 (PARTITION_BASE+i),
289 pptr->pinfo_map[i].dkl_cylno);
290 return (0);
292 if (pptr->pinfo_map[i].dkl_nblk > ((ncyl -
293 pptr->pinfo_map[i].dkl_cylno) * spc())) {
294 err_print(
295 "partition %c: specified # of blocks, %u, "
296 "is out of range\n",
297 (PARTITION_BASE+i),
298 pptr->pinfo_map[i].dkl_nblk);
299 return (0);
303 * Lock out interrupts so the lists don't get mangled.
305 enter_critical();
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 *
321 (nhead * nsect));
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) */
327 exit_critical();
328 fmt_print("\n");
329 return (0);
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
337 * to be created.
340 p_name()
342 char *name;
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");
349 return (-1);
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.
363 enter_critical();
365 * If it was already named, destroy the old name.
367 if (cur_parts->pinfo_name != NULL)
368 destroy_data(cur_parts->pinfo_name);
370 * Set the name.
372 cur_parts->pinfo_name = name;
373 exit_critical();
374 fmt_print("\n");
375 return (0);
380 * This routine implements the 'print' command. It lists the values
381 * for all the partitions in the current partition map.
384 p_print()
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");
391 return (-1);
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);
400 fmt_print("\n");
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);
408 fmt_print(
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 !=
411 NULL)) {
412 fmt_print("Current partition table (%s):\n",
413 cur_parts->pinfo_name);
414 fmt_print(
415 "Total disk sectors available: %llu + %d (reserved sectors)\n\n",
416 cur_parts->etoc->efi_last_u_lba - EFI_MIN_RESV_SIZE,
417 EFI_MIN_RESV_SIZE);
418 } else if (cur_label == L_TYPE_SOLARIS) {
419 fmt_print("Current partition table (unnamed):\n");
420 fmt_print(
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");
424 fmt_print(
425 "Total disk sectors available: %llu + %d (reserved sectors)\n\n",
426 cur_parts->etoc->efi_last_u_lba - EFI_MIN_RESV_SIZE,
427 EFI_MIN_RESV_SIZE);
432 * Print the partition map itself
434 print_map(cur_parts);
435 return (0);
440 * Print a partition map
442 void
443 print_map(struct partition_info *map)
445 int i;
446 int want_header;
447 struct dk_gpt *vtoc64;
449 if (cur_label == L_TYPE_EFI) {
450 vtoc64 = map->etoc;
451 want_header = 1;
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 !=
459 V_RESERVED)) {
460 continue;
463 print_efi_partition(vtoc64, i, want_header);
464 want_header = 0;
466 fmt_print("\n");
467 return;
470 * Loop through each partition, printing the header
471 * the first time.
473 want_header = 1;
474 for (i = 0; i < NDKMAP; i++) {
475 if (i > 9) {
476 break;
478 print_partition(map, i, want_header);
479 want_header = 0;
482 fmt_print("\n");
486 * Print out one line of partition information,
487 * with optional header for EFI type disks.
489 /*ARGSUSED*/
490 void
491 print_efi_partition(struct dk_gpt *map, int partnum, int want_header)
493 int ncyl2_digits = 0;
494 float scaled;
495 char *s;
496 uint64_t secsize;
498 ncyl2_digits = ndigits(map->efi_last_u_lba);
499 if (want_header) {
500 fmt_print("Part ");
501 fmt_print("Tag Flag ");
502 fmt_print("First Sector");
503 nspaces(ncyl2_digits);
504 fmt_print("Size");
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)
513 s = "-";
514 nspaces(10 - (int)strlen(s));
515 fmt_print("%s", s);
517 s = find_string(pflag_choices,
518 (int)map->efi_parts[partnum].p_flag);
519 if (s == (char *)NULL)
520 s = "-";
521 nspaces(6 - (int)strlen(s));
522 fmt_print("%s", s);
524 nspaces(2);
526 secsize = map->efi_parts[partnum].p_size;
527 if (secsize == 0) {
528 fmt_print("%16llu", map->efi_parts[partnum].p_start);
529 nspaces(ncyl2_digits);
530 fmt_print(" 0 ");
531 } else {
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);
539 } else {
540 fmt_print("%8.2fMB", scaled);
543 nspaces(ncyl2_digits);
544 if ((map->efi_parts[partnum].p_start+secsize - 1) ==
545 UINT_MAX64) {
546 fmt_print(" 0 \n");
547 } else {
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.
557 /*ARGSUSED*/
558 void
559 print_partition(struct partition_info *pinfo, int partnum, int want_header)
561 int i;
562 blkaddr_t nblks;
563 int cyl1;
564 int cyl2;
565 float scaled;
566 int maxcyl2;
567 int ncyl2_digits;
568 char *s;
569 blkaddr_t maxnblks = 0;
570 blkaddr_t len;
573 * To align things nicely, we need to know the maximum
574 * width of the number of cylinders field.
576 maxcyl2 = 0;
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;
581 if (nblks > 0) {
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
594 if (want_header) {
595 fmt_print("Part ");
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)
617 s = "-";
618 nspaces(10 - (int)strlen(s));
619 fmt_print("%s", 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)
627 s = "-";
628 nspaces(6 - (int)strlen(s));
629 fmt_print("%s", s);
631 nspaces(2);
633 if (nblks == 0) {
634 fmt_print("%6d ", cyl1);
635 nspaces(ncyl2_digits);
636 fmt_print(" 0 ");
637 } else {
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);
647 } else {
648 fmt_print("%8.2fMB ", scaled);
651 fmt_print("(");
652 pr_dblock(fmt_print, nblks);
653 fmt_print(")");
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;
662 s = zalloc(len);
663 (void) snprintf(s, len, "%s%u%s", " %", ndigits(maxnblks), "u\n");
664 fmt_print(s, nblks);
665 (void) free(s);
670 * Return true if a disk has a volume name
673 chk_volname(disk)
674 struct disk_info *disk;
676 return (disk->v_volume[0] != 0);
681 * Print the volume name, if it appears to be set
683 void
684 print_volname(disk)
685 struct disk_info *disk;
687 int i;
688 char *p;
690 p = disk->v_volume;
691 for (i = 0; i < LEN_DKL_VVOL; i++, p++) {
692 if (*p == 0)
693 break;
694 fmt_print("%c", *p);
700 * Print a number of spaces
702 static void
703 nspaces(n)
704 int n;
706 while (n-- > 0)
707 fmt_print(" ");
711 * Return the number of digits required to print a number
713 static int
714 ndigits(n)
715 uint64_t n;
717 int i;
719 i = 0;
720 while (n > 0) {
721 n /= 10;
722 i++;
725 return (i == 0 ? 1 : i);