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