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
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
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
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
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
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
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
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
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
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
156 p_jpart()
157 {
158 
159 	change_partition(9);
160 	return (0);
161 }
162 #endif	/* defined(i386) */
163 
164 int
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
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
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
383 p_print(void)
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 		unsigned reserved;
415 
416 		reserved = efi_reserved_sectors(cur_parts->etoc);
417 		fmt_print("Current partition table (%s):\n",
418 		    cur_parts->pinfo_name != NULL ?
419 		    cur_parts->pinfo_name : "unnamed");
420 		fmt_print("Total disk sectors available: %llu + %u "
421 		    "(reserved sectors)\n\n",
422 		    cur_parts->etoc->efi_last_u_lba - reserved -
423 		    cur_parts->etoc->efi_first_u_lba + 1, reserved);
424 	}
425 
426 
427 	/*
428 	 * Print the partition map itself
429 	 */
430 	print_map(cur_parts);
431 	return (0);
432 }
433 
434 
435 /*
436  * Print a partition map
437  */
438 void
439 print_map(struct partition_info *map)
440 {
441 	int	i;
442 	int	want_header;
443 	struct	dk_gpt *vtoc64;
444 
445 	if (cur_label == L_TYPE_EFI) {
446 		vtoc64 = map->etoc;
447 		want_header = 1;
448 		for (i = 0; i < vtoc64->efi_nparts; i++) {
449 		/*
450 		 * we want to print partitions above 7 in expert mode only
451 		 * or if the partition is reserved
452 		 */
453 			if (i >= 7 && !expert_mode &&
454 			    ((int)vtoc64->efi_parts[i].p_tag !=
455 			    V_RESERVED)) {
456 				continue;
457 			}
458 
459 			print_efi_partition(vtoc64, i, want_header);
460 			want_header = 0;
461 		}
462 		fmt_print("\n");
463 		return;
464 	}
465 	/*
466 	 * Loop through each partition, printing the header
467 	 * the first time.
468 	 */
469 	want_header = 1;
470 	for (i = 0; i < NDKMAP; i++) {
471 		if (i > 9) {
472 			break;
473 		}
474 		print_partition(map, i, want_header);
475 		want_header = 0;
476 	}
477 
478 	fmt_print("\n");
479 }
480 
481 /*
482  * Print out one line of partition information,
483  * with optional header for EFI type disks.
484  */
485 /*ARGSUSED*/
486 void
487 print_efi_partition(struct dk_gpt *map, int partnum, int want_header)
488 {
489 	int		ncyl2_digits = 0;
490 	float		scaled;
491 	char		*s;
492 	uint64_t	secsize;
493 
494 	ncyl2_digits = ndigits(map->efi_last_u_lba);
495 	if (want_header) {
496 	    fmt_print("Part      ");
497 	    fmt_print("Tag    Flag     ");
498 	    fmt_print("First Sector");
499 	    nspaces(ncyl2_digits);
500 	    fmt_print("Size");
501 	    nspaces(ncyl2_digits);
502 	    fmt_print("Last Sector\n");
503 	}
504 
505 	fmt_print("  %d ", partnum);
506 	s = find_string(ptag_choices,
507 		(int)map->efi_parts[partnum].p_tag);
508 	if (s == (char *)NULL)
509 		s = "-";
510 	nspaces(10 - (int)strlen(s));
511 	fmt_print("%s", s);
512 
513 	s = find_string(pflag_choices,
514 		(int)map->efi_parts[partnum].p_flag);
515 	if (s == (char *)NULL)
516 		s = "-";
517 	nspaces(6 - (int)strlen(s));
518 	fmt_print("%s", s);
519 
520 	nspaces(2);
521 
522 	secsize = map->efi_parts[partnum].p_size;
523 	if (secsize == 0) {
524 	    fmt_print("%16llu", map->efi_parts[partnum].p_start);
525 	    nspaces(ncyl2_digits);
526 	    fmt_print("  0     ");
527 	} else {
528 	    fmt_print("%16llu", map->efi_parts[partnum].p_start);
529 	    scaled = bn2mb(secsize);
530 	    nspaces(ncyl2_digits - 5);
531 	    if (scaled >= (float)1024.0 * 1024) {
532 		fmt_print("%8.2fTB", scaled/((float)1024.0 * 1024));
533 	    } else if (scaled >= (float)1024.0) {
534 		fmt_print("%8.2fGB", scaled/(float)1024.0);
535 	    } else {
536 		fmt_print("%8.2fMB", scaled);
537 	    }
538 	}
539 	nspaces(ncyl2_digits);
540 	if ((map->efi_parts[partnum].p_start+secsize - 1) ==
541 		UINT_MAX64) {
542 	    fmt_print(" 0    \n");
543 	} else {
544 	    fmt_print(" %llu    \n",
545 		map->efi_parts[partnum].p_start+secsize - 1);
546 	}
547 }
548 
549 /*
550  * Print out one line of partition information,
551  * with optional header.
552  */
553 /*ARGSUSED*/
554 void
555 print_partition(struct partition_info *pinfo, int partnum, int want_header)
556 {
557 	int		i;
558 	blkaddr_t	nblks;
559 	int		cyl1;
560 	int		cyl2;
561 	float		scaled;
562 	int		maxcyl2;
563 	int		ncyl2_digits;
564 	char		*s;
565 	blkaddr_t	maxnblks = 0;
566 	blkaddr_t	len;
567 
568 	/*
569 	 * To align things nicely, we need to know the maximum
570 	 * width of the number of cylinders field.
571 	 */
572 	maxcyl2 = 0;
573 	for (i = 0; i < NDKMAP; i++) {
574 		nblks	= (uint_t)pinfo->pinfo_map[i].dkl_nblk;
575 		cyl1	= pinfo->pinfo_map[i].dkl_cylno;
576 		cyl2	= cyl1 + (nblks / spc()) - 1;
577 		if (nblks > 0) {
578 			maxcyl2 = max(cyl2, maxcyl2);
579 			maxnblks = max(nblks, maxnblks);
580 		}
581 	}
582 	/*
583 	 * Get the number of digits required
584 	 */
585 	ncyl2_digits = ndigits(maxcyl2);
586 
587 	/*
588 	 * Print the header, if necessary
589 	 */
590 	if (want_header) {
591 		fmt_print("Part      ");
592 		fmt_print("Tag    Flag     ");
593 		fmt_print("Cylinders");
594 		nspaces(ncyl2_digits);
595 		fmt_print("    Size            Blocks\n");
596 	}
597 
598 	/*
599 	 * Print the partition information
600 	 */
601 	nblks	= pinfo->pinfo_map[partnum].dkl_nblk;
602 	cyl1	= pinfo->pinfo_map[partnum].dkl_cylno;
603 	cyl2	= cyl1 + (nblks / spc()) - 1;
604 
605 	fmt_print("  %x ", partnum);
606 
607 	/*
608 	 * Print the partition tag.  If invalid, print -
609 	 */
610 	s = find_string(ptag_choices,
611 		(int)pinfo->vtoc.v_part[partnum].p_tag);
612 	if (s == (char *)NULL)
613 		s = "-";
614 	nspaces(10 - (int)strlen(s));
615 	fmt_print("%s", s);
616 
617 	/*
618 	 * Print the partition flag.  If invalid print -
619 	 */
620 	s = find_string(pflag_choices,
621 		(int)pinfo->vtoc.v_part[partnum].p_flag);
622 	if (s == (char *)NULL)
623 		s = "-";
624 	nspaces(6 - (int)strlen(s));
625 	fmt_print("%s", s);
626 
627 	nspaces(2);
628 
629 	if (nblks == 0) {
630 		fmt_print("%6d      ", cyl1);
631 		nspaces(ncyl2_digits);
632 		fmt_print("     0         ");
633 	} else {
634 		fmt_print("%6d - ", cyl1);
635 		nspaces(ncyl2_digits - ndigits(cyl2));
636 		fmt_print("%d    ", cyl2);
637 		scaled = bn2mb(nblks);
638 		if (scaled > (float)1024.0 * 1024.0) {
639 			fmt_print("%8.2fTB    ",
640 				scaled/((float)1024.0 * 1024.0));
641 		} else if (scaled > (float)1024.0) {
642 			fmt_print("%8.2fGB    ", scaled/(float)1024.0);
643 		} else {
644 			fmt_print("%8.2fMB    ", scaled);
645 		}
646 	}
647 	fmt_print("(");
648 	pr_dblock(fmt_print, nblks);
649 	fmt_print(")");
650 
651 	nspaces(ndigits(maxnblks/spc()) - ndigits(nblks/spc()));
652 	/*
653 	 * Allocates size of the printf format string.
654 	 * ndigits(ndigits(maxblks)) gives the byte size of
655 	 * the printf width field for maxnblks.
656 	 */
657 	len = strlen(" %") + ndigits(ndigits(maxnblks)) + strlen("d\n") + 1;
658 	s = zalloc(len);
659 	(void) snprintf(s, len, "%s%u%s", " %", ndigits(maxnblks), "u\n");
660 	fmt_print(s, nblks);
661 	(void) free(s);
662 }
663 
664 
665 /*
666  * Return true if a disk has a volume name
667  */
668 int
669 chk_volname(disk)
670 	struct disk_info	*disk;
671 {
672 	return (disk->v_volume[0] != 0);
673 }
674 
675 
676 /*
677  * Print the volume name, if it appears to be set
678  */
679 void
680 print_volname(disk)
681 	struct disk_info	*disk;
682 {
683 	int	i;
684 	char	*p;
685 
686 	p = disk->v_volume;
687 	for (i = 0; i < LEN_DKL_VVOL; i++, p++) {
688 		if (*p == 0)
689 			break;
690 		fmt_print("%c", *p);
691 	}
692 }
693 
694 
695 /*
696  * Print a number of spaces
697  */
698 static void
699 nspaces(n)
700 	int	n;
701 {
702 	while (n-- > 0)
703 		fmt_print(" ");
704 }
705 
706 /*
707  * Return the number of digits required to print a number
708  */
709 static int
710 ndigits(n)
711 	uint64_t	n;
712 {
713 	int	i;
714 
715 	i = 0;
716 	while (n > 0) {
717 		n /= 10;
718 		i++;
719 	}
720 
721 	return (i == 0 ? 1 : i);
722 }
723