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