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