xref: /illumos-gate/usr/src/cmd/format/modify_partition.c (revision 7db575a44a2dd976e52d242cf394a2ba90efbe92)
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 static void	adj_cyl_offset(struct dk_map32 *map);
42 static int	check_map(struct dk_map32 *map);
43 static void	get_user_map(struct dk_map32 *map, int float_part);
44 static void	get_user_map_efi(struct dk_gpt *map, int float_part);
45 
46 /* Padded to 16 entries to quiesce smatch. */
47 static char *partn_list[] = {
48 	"0", "1", "2", "3", "4", "5", "6", "7",
49 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
50 
51 static char *sel_list[] = { "0", "1", "2", "3", NULL };
52 
53 #define	MBYTE	(1024*1024)
54 
55 
56 /*
57  * Modify/Create a predefined partition table.
58  */
59 int
60 p_modify(void)
61 {
62 	struct	partition_info	tmp_pinfo[1];
63 	struct	dk_map32	*map = tmp_pinfo->pinfo_map;
64 	u_ioparam_t		ioparam;
65 	int			inpt_dflt = 0;
66 	int			free_hog = -1;
67 	int			i;
68 	char			tmpstr[80];
69 	char			tmpstr2[300];
70 	int			sel_type = 0;
71 
72 	/*
73 	 * There must be a current disk type (and therefore a current disk).
74 	 */
75 	if (cur_dtype == NULL) {
76 		err_print("Current Disk Type is not set.\n");
77 		return (-1);
78 	}
79 
80 	/*
81 	 * check if there exists a partition table for the disk.
82 	 */
83 	if (cur_parts == NULL) {
84 		err_print("Current Disk has no partition table.\n");
85 		return (-1);
86 	}
87 
88 
89 	/*
90 	 * If the disk has mounted partitions, cannot modify
91 	 */
92 	if (checkmount((diskaddr_t)-1, (diskaddr_t)-1)) {
93 		err_print(
94 "Cannot modify disk partitions while it has mounted partitions.\n\n");
95 		return (-1);
96 	}
97 
98 	/*
99 	 * If the disk has partitions currently being used for
100 	 * swapping, cannot modify
101 	 */
102 	if (checkswap((diskaddr_t)-1, (diskaddr_t)-1)) {
103 		err_print(
104 "Cannot modify disk partitions while it is \
105 currently being used for swapping.\n");
106 		return (-1);
107 	}
108 
109 	/*
110 	 * Check to see if any partitions used for svm, vxvm, ZFS zpool
111 	 * or live upgrade are on the disk.
112 	 */
113 	if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1,
114 	    (diskaddr_t)-1, 0, 0)) {
115 		err_print("Cannot modify disk partition when "
116 		    "partitions are in use as described.\n");
117 		return (-1);
118 	}
119 
120 	/*
121 	 * prompt user for a partition table base
122 	 */
123 	if (cur_parts->pinfo_name != NULL) {
124 		(void) snprintf(tmpstr, sizeof (tmpstr),
125 		    "\t0. Current partition table (%s)",
126 		    cur_parts->pinfo_name);
127 	} else {
128 		(void) sprintf(tmpstr,
129 		    "\t0. Current partition table (unnamed)");
130 	}
131 
132 	(void) snprintf(tmpstr2, sizeof (tmpstr2),
133 	    "Select partitioning base:\n%s\n"
134 	    "\t1. All Free Hog\n"
135 	    "Choose base (enter number) ",
136 	    tmpstr);
137 
138 	ioparam.io_charlist = sel_list;
139 	sel_type = input(FIO_MSTR, tmpstr2, '?', &ioparam,
140 	    &sel_type, DATA_INPUT);
141 
142 	switch (cur_label) {
143 	case L_TYPE_SOLARIS:
144 		if (sel_type == 0) {
145 			/*
146 			 * Check for invalid parameters but do
147 			 * not modify the table.
148 			 */
149 			if (check_map(cur_parts->pinfo_map)) {
150 				err_print("Warning: Fix, or select a "
151 				    "different partition table.\n");
152 				return (0);
153 			}
154 			/*
155 			 * Create partition map from existing map
156 			 */
157 			tmp_pinfo->vtoc = cur_parts->vtoc;
158 			for (i = 0; i < NDKMAP; i++) {
159 				map[i].dkl_nblk =
160 				    cur_parts->pinfo_map[i].dkl_nblk;
161 				map[i].dkl_cylno =
162 				    cur_parts->pinfo_map[i].dkl_cylno;
163 			}
164 		} else {
165 			/*
166 			 * Make an empty partition map, with all the space
167 			 * in the c partition.
168 			 */
169 			set_vtoc_defaults(tmp_pinfo);
170 			for (i = 0; i < NDKMAP; i++) {
171 				map[i].dkl_nblk = 0;
172 				map[i].dkl_cylno = 0;
173 			}
174 			map[C_PARTITION].dkl_nblk = ncyl * spc();
175 
176 #if defined(i386)
177 			/*
178 			 * Adjust for the boot and possibly alternates
179 			 * 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, "Do you wish to continue creating a new "
209 	    "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("Warning: No space available from Free Hog "
251 			    "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, "Okay to make this the current partition table",
299 	    '?', &ioparam, &inpt_dflt, DATA_INPUT)) {
300 		return (0);
301 	} else {
302 		make_partition();
303 		/*
304 		 * Update new partition map
305 		 */
306 		for (i = 0; i < NDKMAP; i++) {
307 			cur_parts->pinfo_map[i].dkl_nblk = map[i].dkl_nblk;
308 			cur_parts->pinfo_map[i].dkl_cylno = map[i].dkl_cylno;
309 #ifdef i386
310 			cur_parts->vtoc.v_part[i].p_start =
311 			    map[i].dkl_cylno * nhead * nsect;
312 			cur_parts->vtoc.v_part[i].p_size =
313 			    map[i].dkl_nblk;
314 #endif
315 		}
316 		(void) p_name();
317 
318 		/*
319 		 * Label the disk now
320 		 */
321 		if (check("Ready to label disk, continue")) {
322 			return (-1);
323 		}
324 		fmt_print("\n");
325 		if (write_label()) {
326 			err_print("Writing label failed\n");
327 			return (-1);
328 		}
329 		return (0);
330 	}
331 }
332 
333 /*
334  * Adjust cylinder offsets
335  */
336 static void
337 adj_cyl_offset(struct dk_map32 *map)
338 {
339 	int	i;
340 	int	cyloffset = 0;
341 
342 
343 	/*
344 	 * Update cylno offsets
345 	 */
346 
347 #if defined(_SUNOS_VTOC_16)
348 	/*
349 	 * Correct cylinder allocation for having the boot and alternates
350 	 * slice in the beginning of the disk
351 	 */
352 	for (i = NDKMAP/2; i < NDKMAP; i++) {
353 		if (i != C_PARTITION && map[i].dkl_nblk) {
354 			map[i].dkl_cylno = cyloffset;
355 			cyloffset += (map[i].dkl_nblk + (spc()-1))/spc();
356 		} else if (map[i].dkl_nblk == 0) {
357 			map[i].dkl_cylno = 0;
358 		}
359 	}
360 	for (i = 0; i < NDKMAP/2; i++) {
361 
362 #else					/* !defined(_SUNOS_VTOC_16) */
363 	for (i = 0; i < NDKMAP; i++) {
364 #endif					/* defined(_SUNOS_VTOC_16) */
365 
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 }
374 
375 
376 /*
377  * Check partition table
378  */
379 static int
380 check_map(struct dk_map32 *map)
381 {
382 	int		i;
383 	diskaddr_t	cyloffset = 0;
384 	diskaddr_t	tot_blks = 0;
385 
386 #ifdef i386
387 	/*
388 	 * On x86, we must account for the boot and alternates
389 	 */
390 	cyloffset = map[0].dkl_cylno;
391 #endif
392 
393 	/*
394 	 * Do some checks for invalid parameters but do
395 	 * not modify the table.
396 	 */
397 	for (i = 0; i < NDKMAP; i++) {
398 		if (map[i].dkl_cylno > (blkaddr32_t)ncyl-1) {
399 			err_print("Warning: Partition %c starting cylinder "
400 			    "%d is out of range.\n",
401 			    (PARTITION_BASE+i), map[i].dkl_cylno);
402 			return (-1);
403 		}
404 		if (map[i].dkl_nblk >
405 		    (diskaddr_t)(ncyl - map[i].dkl_cylno) * spc()) {
406 			err_print("Warning: Partition %c, specified # of "
407 			    "blocks, %u, is out of range.\n",
408 			    (PARTITION_BASE+i), map[i].dkl_nblk);
409 			return (-1);
410 		}
411 		if (i != C_PARTITION && map[i].dkl_nblk) {
412 #ifdef	i386
413 			if (i == I_PARTITION || i == J_PARTITION)
414 				continue;
415 #endif
416 			if (map[i].dkl_cylno < cyloffset) {
417 				err_print("Warning: Overlapping partition "
418 				    "(%c) in table.\n", PARTITION_BASE+i);
419 				return (-1);
420 			} else if (map[i].dkl_cylno > cyloffset) {
421 				err_print("Warning: Non-contiguous partition "
422 				    "(%c) in table.\n", PARTITION_BASE+i);
423 			}
424 			cyloffset +=
425 			    ((diskaddr_t)map[i].dkl_nblk + (spc() - 1)) / spc();
426 			tot_blks += map[i].dkl_nblk;
427 		}
428 	}
429 	if (tot_blks > map[C_PARTITION].dkl_nblk) {
430 		err_print("Warning: Total blocks used is greater than number "
431 		    "of blocks in '%c'\n\tpartition.\n",
432 		    C_PARTITION + PARTITION_BASE);
433 		return (-1);
434 	}
435 	return (0);
436 }
437 
438 
439 
440 /*
441  * get user defined partitions
442  */
443 static void
444 get_user_map(struct dk_map32 *map, int float_part)
445 {
446 	int		i;
447 	blkaddr32_t	newsize;
448 	blkaddr32_t	deflt;
449 	char		tmpstr[80];
450 	u_ioparam_t	ioparam;
451 
452 	/*
453 	 * Get partition sizes
454 	 */
455 	for (i = 0; i < NDKMAP; i++) {
456 		if (partn_list[i] == NULL)
457 			break;
458 		if ((i == C_PARTITION) || (i == float_part)) {
459 			continue;
460 		} else {
461 			ioparam.io_bounds.lower = 0;
462 			ioparam.io_bounds.upper = map[i].dkl_nblk +
463 			    map[float_part].dkl_nblk;
464 			deflt = map[i].dkl_nblk;
465 			if (ioparam.io_bounds.upper == 0) {
466 				err_print("Warning: no space available for "
467 				    "'%s' from Free Hog partition\n",
468 				    partn_list[i]);
469 				continue;
470 			}
471 			(void) snprintf(tmpstr, sizeof (tmpstr),
472 			    "Enter size of partition '%s' ",
473 			    partn_list[i]);
474 			newsize = (blkaddr32_t)input(FIO_CYL, tmpstr, ':',
475 			    &ioparam, (int *)&deflt, DATA_INPUT);
476 			map[float_part].dkl_nblk -= (newsize - map[i].dkl_nblk);
477 			map[i].dkl_nblk = newsize;
478 		}
479 	}
480 }
481 
482 static struct partition_info *
483 build_partition(struct disk_type *tptr)
484 {
485 	struct partition_info	*part;
486 	struct dk_label		*label;
487 	int			i;
488 
489 #ifdef DEBUG
490 	fmt_print("Creating Default Partition for the disk \n");
491 #endif
492 	/*
493 	 * construct a label and pass it on to
494 	 * build_default_partition() which builds the
495 	 * default partition list.
496 	 */
497 	label = zalloc(sizeof (struct dk_label));
498 	label->dkl_pcyl = tptr->dtype_pcyl;
499 	label->dkl_ncyl = tptr->dtype_ncyl;
500 	label->dkl_acyl = tptr->dtype_acyl;
501 	label->dkl_nhead = tptr->dtype_nhead;
502 	label->dkl_nsect = tptr->dtype_nsect;
503 	label->dkl_apc = apc;
504 	label->dkl_intrlv = 1;
505 	label->dkl_rpm	= tptr->dtype_rpm;
506 
507 	if (!build_default_partition(label, cur_ctype->ctype_ctype))
508 		return (NULL);
509 
510 	part = zalloc(sizeof (struct partition_info));
511 	part->pinfo_name = alloc_string(tptr->dtype_asciilabel);
512 	/*
513 	 * Fill in the partition info from the label
514 	 */
515 	for (i = 0; i < NDKMAP; i++) {
516 #if defined(_SUNOS_VTOC_8)
517 		part->pinfo_map[i] = label->dkl_map[i];
518 #else
519 		part->pinfo_map[i].dkl_cylno =
520 		    label->dkl_vtoc.v_part[i].p_start /
521 		    (blkaddr32_t)(tptr->dtype_nhead * tptr->dtype_nsect - apc);
522 		part->pinfo_map[i].dkl_nblk =
523 		    label->dkl_vtoc.v_part[i].p_size;
524 #endif /* ifdefined(_SUNOS_VTOC_8) */
525 	}
526 	part->vtoc = label->dkl_vtoc;
527 	return (part);
528 }
529 
530 /*
531  * build new partition table for given disk type
532  */
533 static void
534 get_user_map_efi(struct dk_gpt *map, int float_part)
535 {
536 	int		i;
537 	efi_deflt_t	efi_deflt;
538 	u_ioparam_t	ioparam;
539 	char		tmpstr[80];
540 	uint64_t	i64;
541 	uint64_t	start_lba = map->efi_first_u_lba;
542 	uint64_t	reserved;
543 
544 	reserved = efi_reserved_sectors(map);
545 	for (i = 0; i < map->efi_nparts - 1; i++) {
546 		/* GPT partition 7 is whole disk device, minor node "wd" */
547 		if (i == float_part || i == 7)
548 			continue;
549 
550 		ioparam.io_bounds.lower = start_lba;
551 		ioparam.io_bounds.upper = map->efi_last_u_lba;
552 		efi_deflt.start_sector = ioparam.io_bounds.lower;
553 		efi_deflt.end_sector = map->efi_parts[i].p_size;
554 		(void) sprintf(tmpstr, "Enter size of partition %d ", i);
555 		i64 = input(FIO_EFI, tmpstr, ':',
556 		    &ioparam, (int *)&efi_deflt, DATA_INPUT);
557 		if (i64 == 0) {
558 			map->efi_parts[i].p_tag = V_UNASSIGNED;
559 		} else if ((i64 != 0) && (map->efi_parts[i].p_tag ==
560 		    V_UNASSIGNED)) {
561 			map->efi_parts[i].p_tag = V_USR;
562 		}
563 		if (i64 == 0) {
564 			map->efi_parts[i].p_start = 0;
565 		} else {
566 			map->efi_parts[i].p_start = start_lba;
567 		}
568 		map->efi_parts[i].p_size = i64;
569 		start_lba += i64;
570 	}
571 	map->efi_parts[float_part].p_start = start_lba;
572 	map->efi_parts[float_part].p_size = map->efi_last_u_lba + 1 -
573 	    start_lba - reserved;
574 	map->efi_parts[float_part].p_tag = V_USR;
575 	if (map->efi_parts[float_part].p_size == 0) {
576 		map->efi_parts[float_part].p_size = 0;
577 		map->efi_parts[float_part].p_start = 0;
578 		map->efi_parts[float_part].p_tag = V_UNASSIGNED;
579 		fmt_print("Warning: No space left for HOG\n");
580 	}
581 
582 	for (i = 0; i < map->efi_nparts; i++) {
583 		if (map->efi_parts[i].p_tag == V_RESERVED) {
584 			map->efi_parts[i].p_start = map->efi_last_u_lba -
585 			    reserved + 1;
586 			map->efi_parts[i].p_size = reserved;
587 			break;
588 		}
589 	}
590 }
591 
592 
593 void
594 new_partitiontable(struct disk_type *tptr, struct disk_type *oldtptr)
595 {
596 	struct partition_info *part;
597 
598 	/*
599 	 * check if disk geometry has changed , if so add new
600 	 * partition table else copy the old partition table.(best guess).
601 	 */
602 	if ((oldtptr != NULL) &&
603 	    (tptr->dtype_ncyl == oldtptr->dtype_ncyl) &&
604 	    (tptr->dtype_nhead == oldtptr->dtype_nhead) &&
605 	    (tptr->dtype_nsect == oldtptr->dtype_nsect)) {
606 		part = (struct partition_info *)
607 		    zalloc(sizeof (struct partition_info));
608 		bcopy((char *)cur_parts, (char *)part,
609 		    sizeof (struct partition_info));
610 		part->pinfo_next = tptr->dtype_plist;
611 		tptr->dtype_plist = part;
612 	} else {
613 
614 #ifdef DEBUG
615 		if (cur_parts != NULL) {
616 			fmt_print("Warning: Partition Table is set");
617 			fmt_print("to default partition table. \n");
618 		}
619 #endif
620 		if (tptr->dtype_plist == NULL) {
621 			part = (struct partition_info *)build_partition(tptr);
622 			if (part != NULL) {
623 				part->pinfo_next = tptr->dtype_plist;
624 				tptr->dtype_plist = part;
625 			}
626 		}
627 	}
628 }
629