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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains functions to implement the partition menu commands.
31  */
32 #include "global.h"
33 #include "partition.h"
34 #include "menu_partition.h"
35 #include "menu_command.h"
36 #include "modify_partition.h"
37 #include "checkdev.h"
38 #include "misc.h"
39 #include "label.h"
40 #include "auto_sense.h"
41 
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #ifdef __STDC__
46 
47 /* Function prototypes for ANSI C Compilers */
48 
49 static void	adj_cyl_offset(struct dk_map32 *map);
50 static int	check_map(struct dk_map32 *map);
51 static void	get_user_map(struct dk_map32 *map, int float_part);
52 static void	get_user_map_efi(struct dk_gpt *map, int float_part);
53 
54 #else	/* __STDC__ */
55 
56 /* Function prototypes for non-ANSI C Compilers */
57 
58 static void	adj_cyl_offset();
59 static int	check_map();
60 static void	get_user_map();
61 static void	get_user_map_efi();
62 
63 #endif	/* __STDC__ */
64 
65 static char *partn_list[] = { "0", "1", "2", "3", "4", "5", "6", "7", NULL };
66 
67 static char *sel_list[] = { "0", "1", "2", "3", NULL };
68 
69 #define	MBYTE	(1024*1024)
70 
71 
72 /*
73  * Modify/Create a predefined partition table.
74  */
75 int
76 p_modify()
77 {
78 	struct	partition_info	tmp_pinfo[1];
79 	struct	dk_map32	*map = tmp_pinfo->pinfo_map;
80 	u_ioparam_t		ioparam;
81 	int			inpt_dflt = 0;
82 	int			free_hog = -1;
83 	int			i;
84 	char			tmpstr[80];
85 	char			tmpstr2[300];
86 	int			sel_type = 0;
87 
88 	/*
89 	 * There must be a current disk type (and therefore a current disk).
90 	 */
91 	if (cur_dtype == NULL) {
92 		err_print("Current Disk Type is not set.\n");
93 		return (-1);
94 	}
95 
96 	/*
97 	 * check if there exists a partition table for the disk.
98 	 */
99 	if (cur_parts == NULL) {
100 		err_print("Current Disk has no partition table.\n");
101 		return (-1);
102 	}
103 
104 
105 	/*
106 	 * If the disk has mounted partitions, cannot modify
107 	 */
108 	if (checkmount((daddr_t)-1, (daddr_t)-1)) {
109 		err_print(
110 "Cannot modify disk partitions while it has mounted partitions.\n\n");
111 		return (-1);
112 	}
113 	/*
114 	 * If the disk has partitions currently being used for
115 	 * swapping, cannot modify
116 	 */
117 	if (checkswap((daddr_t)-1, (daddr_t)-1)) {
118 		err_print(
119 "Cannot modify disk partitions while it is \
120 currently being used for swapping.\n");
121 		return (-1);
122 	}
123 	/*
124 	 * prompt user for a partition table base
125 	 */
126 	if (cur_parts->pinfo_name != NULL) {
127 		(void) snprintf(tmpstr, sizeof (tmpstr),
128 			"\t0. Current partition table (%s)",
129 			cur_parts->pinfo_name);
130 	} else {
131 		(void) sprintf(tmpstr,
132 			"\t0. Current partition table (unnamed)");
133 	}
134 
135 	(void) snprintf(tmpstr2, sizeof (tmpstr2),
136 "Select partitioning base:\n%s\n"
137 "\t1. All Free Hog\n"
138 "Choose base (enter number) ",
139 		tmpstr);
140 
141 	ioparam.io_charlist = sel_list;
142 	sel_type = input(FIO_MSTR, tmpstr2, '?', &ioparam,
143 		&sel_type, DATA_INPUT);
144 
145 	switch (cur_label) {
146 	case L_TYPE_SOLARIS:
147 	    if (sel_type == 0) {
148 		/*
149 		 * Check for invalid parameters but do
150 		 * not modify the table.
151 		 */
152 		if (check_map(cur_parts->pinfo_map)) {
153 			err_print("\
154 Warning: Fix, or select a different partition table.\n");
155 			return (0);
156 		}
157 		/*
158 		 * Create partition map from existing map
159 		 */
160 		tmp_pinfo->vtoc = cur_parts->vtoc;
161 		for (i = 0; i < NDKMAP; i++) {
162 			map[i].dkl_nblk = cur_parts->pinfo_map[i].dkl_nblk;
163 			map[i].dkl_cylno = cur_parts->pinfo_map[i].dkl_cylno;
164 		}
165 	    } else {
166 		/*
167 		 * Make an empty partition map, with all the space
168 		 * in the c partition.
169 		 */
170 		set_vtoc_defaults(tmp_pinfo);
171 		for (i = 0; i < NDKMAP; i++) {
172 			map[i].dkl_nblk = 0;
173 			map[i].dkl_cylno = 0;
174 		}
175 		map[C_PARTITION].dkl_nblk = ncyl * spc();
176 
177 #if defined(i386)
178 		/*
179 		 * Adjust for the boot and possibly alternates partitions
180 		 */
181 		map[I_PARTITION].dkl_nblk = spc();
182 		map[I_PARTITION].dkl_cylno = 0;
183 		if (cur_ctype->ctype_ctype != DKC_SCSI_CCS) {
184 			map[J_PARTITION].dkl_nblk = 2 * spc();
185 			map[J_PARTITION].dkl_cylno = spc() / spc();
186 		}
187 #endif			/* defined(i386) */
188 	    }
189 	    break;
190 	case L_TYPE_EFI:
191 	    if (sel_type == 1) {
192 		for (i = 0; i < cur_parts->etoc->efi_nparts; i++) {
193 		    cur_parts->etoc->efi_parts[i].p_start = 0;
194 		    cur_parts->etoc->efi_parts[i].p_size = 0;
195 		}
196 	    }
197 	    break;
198 	}
199 
200 	fmt_print("\n");
201 	if (cur_label == L_TYPE_SOLARIS) {
202 	    print_map(tmp_pinfo);
203 	} else {
204 	    print_map(cur_parts);
205 	}
206 
207 	ioparam.io_charlist = confirm_list;
208 	if (input(FIO_MSTR,
209 "Do you wish to continue creating a new partition\ntable based on above table",
210 			'?', &ioparam, &inpt_dflt, DATA_INPUT)) {
211 		return (0);
212 	}
213 
214 	/*
215 	 * get Free Hog partition
216 	 */
217 	inpt_dflt = 1;
218 	while ((free_hog < 0) && (cur_label == L_TYPE_SOLARIS)) {
219 		free_hog = G_PARTITION;	/* default to g partition */
220 		ioparam.io_charlist = partn_list;
221 		free_hog = input(FIO_MSTR, "Free Hog partition", '?',
222 			&ioparam, &free_hog, DATA_INPUT);
223 		/* disallow c partition */
224 		if (free_hog == C_PARTITION) {
225 			fmt_print("'%c' cannot be the 'Free Hog' partition.\n",
226 				C_PARTITION + PARTITION_BASE);
227 			free_hog = -1;
228 			continue;
229 		}
230 		/*
231 		 * If user selected all float set the
232 		 * float to be the whole disk.
233 		 */
234 		if (sel_type == 1) {
235 			map[free_hog].dkl_nblk = map[C_PARTITION].dkl_nblk;
236 #if defined(i386)
237 			map[free_hog].dkl_nblk -= map[I_PARTITION].dkl_nblk;
238 			if (cur_ctype->ctype_ctype != DKC_SCSI_CCS) {
239 				map[free_hog].dkl_nblk -=
240 					map[J_PARTITION].dkl_nblk;
241 			}
242 #endif			/* defined(i386) */
243 			break;
244 		}
245 		/*
246 		 * Warn the user if there is no free space in
247 		 * the float partition.
248 		 */
249 		if (map[free_hog].dkl_nblk == 0) {
250 			err_print("\
251 Warning: No space available from Free Hog partition.\n");
252 			ioparam.io_charlist = confirm_list;
253 			if (input(FIO_MSTR, "Continue", '?',
254 				&ioparam, &inpt_dflt, DATA_INPUT)) {
255 				free_hog = -1;
256 			}
257 		}
258 	}
259 	inpt_dflt = 0;
260 
261 	if (cur_label == L_TYPE_EFI) {
262 	    free_hog = G_PARTITION; /* default to g partition */
263 	    ioparam.io_charlist = partn_list;
264 	    free_hog = input(FIO_MSTR, "Free Hog partition", '?',
265 		&ioparam, &free_hog, DATA_INPUT);
266 	    /* disallow c partition */
267 	    if (free_hog == C_PARTITION) {
268 		fmt_print("'%c' cannot be the 'Free Hog' partition.\n",
269 		    C_PARTITION + PARTITION_BASE);
270 		return (-1);
271 	    }
272 	    get_user_map_efi(cur_parts->etoc, free_hog);
273 	    print_map(cur_parts);
274 	    if (check("Ready to label disk, continue")) {
275 		return (-1);
276 	    }
277 	    fmt_print("\n");
278 	    if (write_label()) {
279 		err_print("Writing label failed\n");
280 		return (-1);
281 	    }
282 	    return (0);
283 	}
284 	/*
285 	 * get user modified partition table
286 	 */
287 	get_user_map(map, free_hog);
288 
289 	/*
290 	 * Update cylno offsets
291 	 */
292 	adj_cyl_offset(map);
293 
294 	fmt_print("\n");
295 	print_map(tmp_pinfo);
296 
297 	ioparam.io_charlist = confirm_list;
298 	if (input(FIO_MSTR, "\
299 Okay to make this the current partition table", '?',
300 		&ioparam, &inpt_dflt, DATA_INPUT)) {
301 		return (0);
302 	} else {
303 		make_partition();
304 		/*
305 		 * Update new partition map
306 		 */
307 		for (i = 0; i < NDKMAP; i++) {
308 			cur_parts->pinfo_map[i].dkl_nblk = map[i].dkl_nblk;
309 			cur_parts->pinfo_map[i].dkl_cylno = map[i].dkl_cylno;
310 #ifdef i386
311 			cur_parts->vtoc.v_part[i].p_start =
312 				map[i].dkl_cylno * nhead * nsect;
313 			cur_parts->vtoc.v_part[i].p_size =
314 				map[i].dkl_nblk;
315 #endif
316 		}
317 		(void) p_name();
318 
319 		/*
320 		 * Label the disk now
321 		 */
322 		if (check("Ready to label disk, continue")) {
323 			return (-1);
324 		}
325 		fmt_print("\n");
326 		if (write_label()) {
327 			err_print("Writing label failed\n");
328 			return (-1);
329 		}
330 		return (0);
331 	}
332 }
333 
334 
335 
336 /*
337  * Adjust cylinder offsets
338  */
339 static void
340 adj_cyl_offset(map)
341 	struct	dk_map32 *map;
342 {
343 	int	i;
344 	int	cyloffset = 0;
345 
346 
347 	/*
348 	 * Update cylno offsets
349 	 */
350 
351 #if defined(_SUNOS_VTOC_16)
352 	/*
353 	 * Correct cylinder allocation for having the boot and alternates
354 	 * slice in the beginning of the disk
355 	 */
356 	for (i = NDKMAP/2; i < NDKMAP; i++) {
357 		if (i != C_PARTITION && map[i].dkl_nblk) {
358 			map[i].dkl_cylno = cyloffset;
359 			cyloffset += (map[i].dkl_nblk + (spc()-1))/spc();
360 		} else if (map[i].dkl_nblk == 0) {
361 			map[i].dkl_cylno = 0;
362 		}
363 	}
364 	for (i = 0; i < NDKMAP/2; i++) {
365 
366 #else					/* !defined(_SUNOS_VTOC_16) */
367 	for (i = 0; i < NDKMAP; i++) {
368 #endif					/* defined(_SUNOS_VTOC_16) */
369 
370 		if (i != C_PARTITION && map[i].dkl_nblk) {
371 			map[i].dkl_cylno = cyloffset;
372 			cyloffset += (map[i].dkl_nblk + (spc()-1))/spc();
373 		} else if (map[i].dkl_nblk == 0) {
374 			map[i].dkl_cylno = 0;
375 		}
376 	}
377 }
378 
379 
380 /*
381  * Check partition table
382  */
383 static int
384 check_map(map)
385 	struct	dk_map32 *map;
386 {
387 	int	i;
388 	int	cyloffset = 0;
389 	int	tot_blks = 0;
390 
391 #ifdef i386
392 	/*
393 	 * On x86, we must account for the boot and alternates
394 	 */
395 	cyloffset = map[0].dkl_cylno;
396 	tot_blks = map[0].dkl_nblk;
397 #endif
398 
399 	/*
400 	 * Do some checks for invalid parameters but do
401 	 * not modify the table.
402 	 */
403 	for (i = 0; i < NDKMAP; i++) {
404 		if (map[i].dkl_cylno < 0 ||
405 				map[i].dkl_cylno > (daddr_t)ncyl-1) {
406 			err_print("\
407 Warning: Partition %c starting cylinder %d is out of range.\n",
408 				(PARTITION_BASE+i), map[i].dkl_cylno);
409 			return (-1);
410 		}
411 		if (map[i].dkl_nblk < 0 || map[i].dkl_nblk > (daddr_t)(ncyl -
412 			map[i].dkl_cylno) * spc()) {
413 			err_print("\
414 Warning: Partition %c, specified # of blocks, %d, is out of range.\n",
415 				(PARTITION_BASE+i), map[i].dkl_nblk);
416 			return (-1);
417 		}
418 		if (i != C_PARTITION && map[i].dkl_nblk) {
419 #ifdef	i386
420 			if (i == I_PARTITION || i == J_PARTITION)
421 				continue;
422 #endif
423 			if (map[i].dkl_cylno < cyloffset) {
424 				err_print(
425 "Warning: Overlapping partition (%c) in table.\n", PARTITION_BASE+i);
426 				return (-1);
427 			} else if (map[i].dkl_cylno > cyloffset) {
428 				err_print(
429 "Warning: Non-contiguous partition (%c) in table.\n", PARTITION_BASE+i);
430 			}
431 			cyloffset += (map[i].dkl_nblk + (spc()-1))/spc();
432 			tot_blks = map[i].dkl_nblk;
433 		}
434 	}
435 	if (tot_blks > map[C_PARTITION].dkl_nblk) {
436 		err_print("\
437 Warning: Total blocks used is greater than number of blocks in '%c'\n\
438 \tpartition.\n", C_PARTITION + PARTITION_BASE);
439 	return (-1);
440 	}
441 	return (0);
442 }
443 
444 
445 
446 /*
447  * get user defined partitions
448  */
449 static void
450 get_user_map(map, float_part)
451 	struct	dk_map32 *map;
452 	int	float_part;
453 {
454 	int		i;
455 	int		newsize;
456 	int		deflt;
457 	char		tmpstr[80];
458 	u_ioparam_t	ioparam;
459 
460 	/*
461 	 * Get partition sizes
462 	 */
463 	for (i = 0; i < NDKMAP; i++) {
464 		if (partn_list[i] == NULL)
465 			break;
466 		if ((i == C_PARTITION) || (i == float_part))
467 			continue;
468 		else {
469 			ioparam.io_bounds.lower = 0;
470 			ioparam.io_bounds.upper = map[i].dkl_nblk +
471 				map[float_part].dkl_nblk;
472 			deflt = map[i].dkl_nblk;
473 			if (ioparam.io_bounds.upper == 0) {
474 				err_print("\
475 Warning: no space available for '%s' from Free Hog partition\n",
476 					partn_list[i]);
477 				continue;
478 			}
479 			(void) snprintf(tmpstr, sizeof (tmpstr),
480 				"Enter size of partition '%s' ",
481 				partn_list[i]);
482 			newsize = input(FIO_CYL, tmpstr, ':',
483 				&ioparam, &deflt, DATA_INPUT);
484 			map[float_part].dkl_nblk -= (newsize - map[i].dkl_nblk);
485 			map[i].dkl_nblk = newsize;
486 		}
487 	}
488 }
489 
490 static struct partition_info *
491 build_partition(tptr)
492 struct disk_type *tptr;
493 {
494 	struct partition_info	*part;
495 	struct dk_label		*label;
496 	int			i;
497 
498 #ifdef DEBUG
499 	fmt_print("Creating Default Partition for the disk \n");
500 #endif
501 	/*
502 	 * construct a label and pass it on to
503 	 * build_default_partition() which builds the
504 	 * default partition list.
505 	 */
506 	label = zalloc(sizeof (struct dk_label));
507 	label->dkl_pcyl = tptr->dtype_pcyl;
508 	label->dkl_ncyl = tptr->dtype_ncyl;
509 	label->dkl_acyl = tptr->dtype_acyl;
510 	label->dkl_nhead = tptr->dtype_nhead;
511 	label->dkl_nsect = tptr->dtype_nsect;
512 	label->dkl_apc = apc;
513 	label->dkl_intrlv = 1;
514 	label->dkl_rpm	= tptr->dtype_rpm;
515 
516 	if (!build_default_partition(label, cur_ctype->ctype_ctype))
517 		return (NULL);
518 
519 	part = (struct partition_info *)
520 		    zalloc(sizeof (struct partition_info));
521 	part->pinfo_name = alloc_string(tptr->dtype_asciilabel);
522 	/*
523 	 * Fill in the partition info from the label
524 	 */
525 	for (i = 0; i < NDKMAP; i++) {
526 #if defined(_SUNOS_VTOC_8)
527 	    part->pinfo_map[i] = label->dkl_map[i];
528 #else
529 	    part->pinfo_map[i].dkl_cylno =
530 	    label->dkl_vtoc.v_part[i].p_start /
531 			((int)(tptr->dtype_nhead * tptr->dtype_nsect - apc));
532 	    part->pinfo_map[i].dkl_nblk =
533 		label->dkl_vtoc.v_part[i].p_size;
534 #endif /* ifdefined(_SUNOS_VTOC_8) */
535 	}
536 	part->vtoc = label->dkl_vtoc;
537 	return (part);
538 }
539 
540 /*
541  * build new partition table for given disk type
542  */
543 static void
544 get_user_map_efi(map, float_part)
545 	struct dk_gpt *map;
546 	int	float_part;
547 {
548 
549 	int		i;
550 	efi_deflt_t	efi_deflt;
551 	u_ioparam_t	ioparam;
552 	char		tmpstr[80];
553 	uint64_t	i64;
554 	uint64_t	start_lba = 34;
555 
556 	for (i = 0; i < map->efi_nparts - 1; i++) {
557 		if (i == float_part)
558 			continue;
559 		else {
560 			ioparam.io_bounds.lower = start_lba;
561 			ioparam.io_bounds.upper = map->efi_last_u_lba;
562 			efi_deflt.start_sector = ioparam.io_bounds.lower;
563 			efi_deflt.end_sector = map->efi_parts[i].p_size;
564 			(void) sprintf(tmpstr,
565 			    "Enter size of partition %d ", i);
566 			i64 = input(FIO_EFI, tmpstr, ':',
567 			    &ioparam, (int *)&efi_deflt, DATA_INPUT);
568 			if (i64 == 0) {
569 			    map->efi_parts[i].p_tag = V_UNASSIGNED;
570 			} else if ((i64 != 0) && (map->efi_parts[i].p_tag ==
571 				V_UNASSIGNED)) {
572 			    map->efi_parts[i].p_tag = V_USR;
573 			}
574 			if (i64 == 0) {
575 			    map->efi_parts[i].p_start = 0;
576 			} else {
577 			    map->efi_parts[i].p_start = start_lba;
578 			}
579 			map->efi_parts[i].p_size = i64;
580 			start_lba += i64;
581 		}
582 	}
583 		map->efi_parts[float_part].p_start = start_lba;
584 		map->efi_parts[float_part].p_size = map->efi_last_u_lba -
585 			start_lba - (1024 * 16);
586 		map->efi_parts[float_part].p_tag = V_USR;
587 		if (map->efi_parts[float_part].p_size == UINT_MAX64) {
588 			map->efi_parts[float_part].p_size = 0;
589 			map->efi_parts[float_part].p_start = 0;
590 			map->efi_parts[float_part].p_tag = V_UNASSIGNED;
591 			fmt_print("Warning: No space left for HOG\n");
592 		}
593 
594 		for (i = 0; i < map->efi_nparts; i++) {
595 		    if (map->efi_parts[i].p_tag == V_RESERVED) {
596 			map->efi_parts[i].p_start = map->efi_last_u_lba -
597 			    (1024 * 16);
598 			map->efi_parts[i].p_size = (1024 * 16);
599 			break;
600 		    }
601 		}
602 }
603 
604 
605 void
606 new_partitiontable(tptr, oldtptr)
607 struct disk_type	*tptr, *oldtptr;
608 {
609 	struct partition_info *part;
610 
611 	/*
612 	 * check if disk geometry has changed , if so add new
613 	 * partition table else copy the old partition table.(best guess).
614 	 */
615 	if ((oldtptr != NULL) &&
616 		(tptr->dtype_ncyl ==  oldtptr->dtype_ncyl) &&
617 		(tptr->dtype_nhead == oldtptr->dtype_nhead) &&
618 		(tptr->dtype_nsect == oldtptr->dtype_nsect)) {
619 
620 	    part = (struct partition_info *)
621 			zalloc(sizeof (struct partition_info));
622 	    bcopy((char *)cur_parts, (char *)part,
623 			sizeof (struct partition_info));
624 	    part->pinfo_next = tptr->dtype_plist;
625 	    tptr->dtype_plist = part;
626 	} else {
627 
628 #ifdef DEBUG
629 		if (cur_parts != NULL) {
630 			fmt_print("Warning: Partition Table is set");
631 			fmt_print("to default partition table. \n");
632 		}
633 #endif
634 		if (tptr->dtype_plist == NULL) {
635 			part = (struct partition_info *)build_partition(tptr);
636 			if (part != NULL) {
637 				part->pinfo_next = tptr->dtype_plist;
638 				tptr->dtype_plist = part;
639 			}
640 		}
641 	}
642 }
643