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