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