xref: /illumos-gate/usr/src/cmd/format/menu_partition.c (revision 269e59f9a28bf47e0f463e64fc5af4a408b73b21)
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()
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
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
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
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
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
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
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
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