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