1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * This file contains functions to implement the partition menu commands.
27 */
28 #include "global.h"
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "partition.h"
33 #include "menu_partition.h"
34 #include "menu_command.h"
35 #include "misc.h"
36 #include "param.h"
37
38 #ifdef __STDC__
39
40 /* Function prototypes for ANSI C Compilers */
41 static void nspaces(int);
42 static int ndigits(uint64_t);
43
44 #else /* __STDC__ */
45
46 /* Function prototypes for non-ANSI C Compilers */
47 static void nspaces();
48 static int ndigits();
49
50 #endif /* __STDC__ */
51
52 /*
53 * This routine implements the 'a' command. It changes the 'a' partition.
54 */
55 int
p_apart()56 p_apart()
57 {
58
59 change_partition(0);
60 return (0);
61 }
62
63 /*
64 * This routine implements the 'b' command. It changes the 'b' partition.
65 */
66 int
p_bpart()67 p_bpart()
68 {
69
70 change_partition(1);
71 return (0);
72 }
73
74 /*
75 * This routine implements the 'c' command. It changes the 'c' partition.
76 */
77 int
p_cpart()78 p_cpart()
79 {
80
81 change_partition(2);
82 return (0);
83 }
84
85 /*
86 * This routine implements the 'd' command. It changes the 'd' partition.
87 */
88 int
p_dpart()89 p_dpart()
90 {
91
92 change_partition(3);
93 return (0);
94 }
95
96 /*
97 * This routine implements the 'e' command. It changes the 'e' partition.
98 */
99 int
p_epart()100 p_epart()
101 {
102
103 change_partition(4);
104 return (0);
105 }
106
107 /*
108 * This routine implements the 'f' command. It changes the 'f' partition.
109 */
110 int
p_fpart()111 p_fpart()
112 {
113
114 change_partition(5);
115 return (0);
116 }
117
118 /*
119 * This routine implements the 'g' command. It changes the 'g' partition.
120 */
121 int
p_gpart()122 p_gpart()
123 {
124
125 change_partition(6);
126 return (0);
127 }
128
129 /*
130 * This routine implements the 'h' command. It changes the 'h' partition.
131 */
132 int
p_hpart()133 p_hpart()
134 {
135
136 change_partition(7);
137 return (0);
138 }
139
140 /*
141 * This routine implements the 'i' command. It is valid only for EFI
142 * labeled disks. This can be used only in expert mode.
143 */
144 int
p_ipart()145 p_ipart()
146 {
147 change_partition(8);
148 return (0);
149 }
150
151 #if defined(i386)
152 /*
153 * This routine implements the 'j' command. It changes the 'j' partition.
154 */
155 int
p_jpart()156 p_jpart()
157 {
158
159 change_partition(9);
160 return (0);
161 }
162 #endif /* defined(i386) */
163
164 int
p_expand()165 p_expand()
166 {
167 uint64_t delta;
168 uint_t nparts;
169 struct dk_gpt *efi_label = cur_parts->etoc;
170
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");
175 return (0);
176 }
177
178 delta = efi_label->efi_last_lba - efi_label->efi_altern_lba;
179 nparts = efi_label->efi_nparts;
180
181 enter_critical();
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;
185 exit_critical();
186
187 fmt_print("The expanded capacity is added to the unallocated space.\n");
188 return (0);
189 }
190
191 /*
192 * This routine implements the 'select' command. It allows the user
193 * to make a pre-defined partition map the current map.
194 */
195 int
p_select()196 p_select()
197 {
198 struct partition_info *pptr, *parts;
199 u_ioparam_t ioparam;
200 int i, index, deflt, *defltptr = NULL;
201 blkaddr_t b_cylno;
202 #if defined(i386)
203 blkaddr_t cyl_offset;
204 #endif
205
206 parts = cur_dtype->dtype_plist;
207 /*
208 * If there are no pre-defined maps for this disk type, it's
209 * an error.
210 */
211 if (parts == NULL) {
212 err_print("No defined partition tables.\n");
213 return (-1);
214 }
215
216 /*
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.
220 */
221 for (i = 0, pptr = parts; pptr != NULL; pptr = pptr->pinfo_next) {
222 if (cur_parts == pptr) {
223 deflt = i;
224 defltptr = &deflt;
225 }
226 if (pptr->pinfo_name == NULL)
227 fmt_print(" %d. unnamed\n", i++);
228 else
229 fmt_print(" %d. %s\n", i++, pptr->pinfo_name);
230 }
231 ioparam.io_bounds.lower = 0;
232 ioparam.io_bounds.upper = i - 1;
233 /*
234 * Ask which map should be made current.
235 */
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)
239 ;
240 if (cur_label == L_TYPE_EFI) {
241 enter_critical();
242 cur_disk->disk_parts = cur_parts = pptr;
243 exit_critical();
244 fmt_print("\n");
245 return (0);
246 }
247 #if defined(i386)
248 /*
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.
253 */
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());
259 }
260 #else /* !defined(i386) */
261
262 b_cylno = 0;
263
264 #endif /* defined(i386) */
265
266 /*
267 * Before we blow the current map away, do some limits checking.
268 */
269 for (i = 0; i < NDKMAP; i++) {
270
271 #if defined(i386)
272 if (i == I_PARTITION || i == J_PARTITION || i == C_PARTITION) {
273 b_cylno = 0;
274 } else if (pptr->pinfo_map[i].dkl_nblk == 0) {
275 /*
276 * Always accept starting cyl 0 if the size is 0 also
277 */
278 b_cylno = 0;
279 } else {
280 b_cylno = cyl_offset;
281 }
282 #endif /* defined(i386) */
283 if (pptr->pinfo_map[i].dkl_cylno < b_cylno ||
284 pptr->pinfo_map[i].dkl_cylno > (ncyl-1)) {
285 err_print(
286 "partition %c: starting cylinder %d is out of range\n",
287 (PARTITION_BASE+i),
288 pptr->pinfo_map[i].dkl_cylno);
289 return (0);
290 }
291 if (pptr->pinfo_map[i].dkl_nblk > ((ncyl -
292 pptr->pinfo_map[i].dkl_cylno) * spc())) {
293 err_print(
294 "partition %c: specified # of blocks, %u, "
295 "is out of range\n",
296 (PARTITION_BASE+i),
297 pptr->pinfo_map[i].dkl_nblk);
298 return (0);
299 }
300 }
301 /*
302 * Lock out interrupts so the lists don't get mangled.
303 */
304 enter_critical();
305 /*
306 * If the old current map is unnamed, delete it.
307 */
308 if (cur_parts != NULL && cur_parts != pptr &&
309 cur_parts->pinfo_name == NULL)
310 delete_partition(cur_parts);
311 /*
312 * Make the selected map current.
313 */
314 cur_disk->disk_parts = cur_parts = pptr;
315
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 *
320 (nhead * nsect));
321 cur_parts->vtoc.v_part[i].p_size =
322 (blkaddr_t)cur_parts->pinfo_map[i].dkl_nblk;
323 }
324 #endif /* defined(_SUNOS_VTOC_16) */
325
326 exit_critical();
327 fmt_print("\n");
328 return (0);
329 }
330
331 /*
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
336 * to be created.
337 */
338 int
p_name()339 p_name()
340 {
341 char *name;
342
343 /*
344 * check if there exists a partition table for the disk.
345 */
346 if (cur_parts == NULL) {
347 err_print("Current Disk has no partition table.\n");
348 return (-1);
349 }
350
351
352 /*
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.
355 */
356 name = (char *)(uintptr_t)input(FIO_OSTR,
357 "Enter table name (remember quotes)",
358 ':', (u_ioparam_t *)NULL, (int *)NULL, DATA_INPUT);
359 /*
360 * Lock out interrupts.
361 */
362 enter_critical();
363 /*
364 * If it was already named, destroy the old name.
365 */
366 if (cur_parts->pinfo_name != NULL)
367 destroy_data(cur_parts->pinfo_name);
368 /*
369 * Set the name.
370 */
371 cur_parts->pinfo_name = name;
372 exit_critical();
373 fmt_print("\n");
374 return (0);
375 }
376
377
378 /*
379 * This routine implements the 'print' command. It lists the values
380 * for all the partitions in the current partition map.
381 */
382 int
p_print()383 p_print()
384 {
385 /*
386 * check if there exists a partition table for the disk.
387 */
388 if (cur_parts == NULL) {
389 err_print("Current Disk has no partition table.\n");
390 return (-1);
391 }
392
393 /*
394 * Print the volume name, if it appears to be set
395 */
396 if (chk_volname(cur_disk)) {
397 fmt_print("Volume: ");
398 print_volname(cur_disk);
399 fmt_print("\n");
400 }
401 /*
402 * Print the name of the current map.
403 */
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);
421 }
422
423
424 /*
425 * Print the partition map itself
426 */
427 print_map(cur_parts);
428 return (0);
429 }
430
431
432 /*
433 * Print a partition map
434 */
435 void
print_map(struct partition_info * map)436 print_map(struct partition_info *map)
437 {
438 int i;
439 int want_header;
440 struct dk_gpt *vtoc64;
441
442 if (cur_label == L_TYPE_EFI) {
443 vtoc64 = map->etoc;
444 want_header = 1;
445 for (i = 0; i < vtoc64->efi_nparts; i++) {
446 /*
447 * we want to print partitions above 7 in expert mode only
448 * or if the partition is reserved
449 */
450 if (i >= 7 && !expert_mode &&
451 ((int)vtoc64->efi_parts[i].p_tag !=
452 V_RESERVED)) {
453 continue;
454 }
455
456 print_efi_partition(vtoc64, i, want_header);
457 want_header = 0;
458 }
459 fmt_print("\n");
460 return;
461 }
462 /*
463 * Loop through each partition, printing the header
464 * the first time.
465 */
466 want_header = 1;
467 for (i = 0; i < NDKMAP; i++) {
468 if (i > 9) {
469 break;
470 }
471 print_partition(map, i, want_header);
472 want_header = 0;
473 }
474
475 fmt_print("\n");
476 }
477
478 /*
479 * Print out one line of partition information,
480 * with optional header for EFI type disks.
481 */
482 /*ARGSUSED*/
483 void
print_efi_partition(struct dk_gpt * map,int partnum,int want_header)484 print_efi_partition(struct dk_gpt *map, int partnum, int want_header)
485 {
486 int ncyl2_digits = 0;
487 float scaled;
488 char *s;
489 uint64_t secsize;
490
491 ncyl2_digits = ndigits(map->efi_last_u_lba);
492 if (want_header) {
493 fmt_print("Part ");
494 fmt_print("Tag Flag ");
495 fmt_print("First Sector");
496 nspaces(ncyl2_digits);
497 fmt_print("Size");
498 nspaces(ncyl2_digits);
499 fmt_print("Last Sector\n");
500 }
501
502 fmt_print(" %d ", partnum);
503 s = find_string(ptag_choices,
504 (int)map->efi_parts[partnum].p_tag);
505 if (s == (char *)NULL)
506 s = "-";
507 nspaces(10 - (int)strlen(s));
508 fmt_print("%s", s);
509
510 s = find_string(pflag_choices,
511 (int)map->efi_parts[partnum].p_flag);
512 if (s == (char *)NULL)
513 s = "-";
514 nspaces(6 - (int)strlen(s));
515 fmt_print("%s", s);
516
517 nspaces(2);
518
519 secsize = map->efi_parts[partnum].p_size;
520 if (secsize == 0) {
521 fmt_print("%16llu", map->efi_parts[partnum].p_start);
522 nspaces(ncyl2_digits);
523 fmt_print(" 0 ");
524 } else {
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);
532 } else {
533 fmt_print("%8.2fMB", scaled);
534 }
535 }
536 nspaces(ncyl2_digits);
537 if ((map->efi_parts[partnum].p_start+secsize - 1) ==
538 UINT_MAX64) {
539 fmt_print(" 0 \n");
540 } else {
541 fmt_print(" %llu \n",
542 map->efi_parts[partnum].p_start+secsize - 1);
543 }
544 }
545
546 /*
547 * Print out one line of partition information,
548 * with optional header.
549 */
550 /*ARGSUSED*/
551 void
print_partition(struct partition_info * pinfo,int partnum,int want_header)552 print_partition(struct partition_info *pinfo, int partnum, int want_header)
553 {
554 int i;
555 blkaddr_t nblks;
556 int cyl1;
557 int cyl2;
558 float scaled;
559 int maxcyl2;
560 int ncyl2_digits;
561 char *s;
562 blkaddr_t maxnblks = 0;
563 blkaddr_t len;
564
565 /*
566 * To align things nicely, we need to know the maximum
567 * width of the number of cylinders field.
568 */
569 maxcyl2 = 0;
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;
574 if (nblks > 0) {
575 maxcyl2 = max(cyl2, maxcyl2);
576 maxnblks = max(nblks, maxnblks);
577 }
578 }
579 /*
580 * Get the number of digits required
581 */
582 ncyl2_digits = ndigits(maxcyl2);
583
584 /*
585 * Print the header, if necessary
586 */
587 if (want_header) {
588 fmt_print("Part ");
589 fmt_print("Tag Flag ");
590 fmt_print("Cylinders");
591 nspaces(ncyl2_digits);
592 fmt_print(" Size Blocks\n");
593 }
594
595 /*
596 * Print the partition information
597 */
598 nblks = pinfo->pinfo_map[partnum].dkl_nblk;
599 cyl1 = pinfo->pinfo_map[partnum].dkl_cylno;
600 cyl2 = cyl1 + (nblks / spc()) - 1;
601
602 fmt_print(" %x ", partnum);
603
604 /*
605 * Print the partition tag. If invalid, print -
606 */
607 s = find_string(ptag_choices,
608 (int)pinfo->vtoc.v_part[partnum].p_tag);
609 if (s == (char *)NULL)
610 s = "-";
611 nspaces(10 - (int)strlen(s));
612 fmt_print("%s", s);
613
614 /*
615 * Print the partition flag. If invalid print -
616 */
617 s = find_string(pflag_choices,
618 (int)pinfo->vtoc.v_part[partnum].p_flag);
619 if (s == (char *)NULL)
620 s = "-";
621 nspaces(6 - (int)strlen(s));
622 fmt_print("%s", s);
623
624 nspaces(2);
625
626 if (nblks == 0) {
627 fmt_print("%6d ", cyl1);
628 nspaces(ncyl2_digits);
629 fmt_print(" 0 ");
630 } else {
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);
640 } else {
641 fmt_print("%8.2fMB ", scaled);
642 }
643 }
644 fmt_print("(");
645 pr_dblock(fmt_print, nblks);
646 fmt_print(")");
647
648 nspaces(ndigits(maxnblks/spc()) - ndigits(nblks/spc()));
649 /*
650 * Allocates size of the printf format string.
651 * ndigits(ndigits(maxblks)) gives the byte size of
652 * the printf width field for maxnblks.
653 */
654 len = strlen(" %") + ndigits(ndigits(maxnblks)) + strlen("d\n") + 1;
655 s = zalloc(len);
656 (void) snprintf(s, len, "%s%u%s", " %", ndigits(maxnblks), "u\n");
657 fmt_print(s, nblks);
658 (void) free(s);
659 }
660
661
662 /*
663 * Return true if a disk has a volume name
664 */
665 int
chk_volname(disk)666 chk_volname(disk)
667 struct disk_info *disk;
668 {
669 return (disk->v_volume[0] != 0);
670 }
671
672
673 /*
674 * Print the volume name, if it appears to be set
675 */
676 void
print_volname(disk)677 print_volname(disk)
678 struct disk_info *disk;
679 {
680 int i;
681 char *p;
682
683 p = disk->v_volume;
684 for (i = 0; i < LEN_DKL_VVOL; i++, p++) {
685 if (*p == 0)
686 break;
687 fmt_print("%c", *p);
688 }
689 }
690
691
692 /*
693 * Print a number of spaces
694 */
695 static void
nspaces(n)696 nspaces(n)
697 int n;
698 {
699 while (n-- > 0)
700 fmt_print(" ");
701 }
702
703 /*
704 * Return the number of digits required to print a number
705 */
706 static int
ndigits(n)707 ndigits(n)
708 uint64_t n;
709 {
710 int i;
711
712 i = 0;
713 while (n > 0) {
714 n /= 10;
715 i++;
716 }
717
718 return (i == 0 ? 1 : i);
719 }
720