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
45static void	adj_cyl_offset(struct dk_map32 *map);
46static int	check_map(struct dk_map32 *map);
47static void	get_user_map(struct dk_map32 *map, int float_part);
48static 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
54static void	adj_cyl_offset();
55static int	check_map();
56static void	get_user_map();
57static void	get_user_map_efi();
58
59#endif	/* __STDC__ */
60
61static char *partn_list[] = { "0", "1", "2", "3", "4", "5", "6", "7", NULL };
62
63static 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 */
71int
72p_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 \
117currently 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("\
163Warning: 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("\
260Warning: 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, "\
308Okay 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 */
348static void
349adj_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 */
392static int
393check_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("\
415Warning: 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("\
422Warning: 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("\
445Warning: 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 */
457static void
458get_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("\
483Warning: 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
498static struct partition_info *
499build_partition(tptr)
500struct 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 */
551static void
552get_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
613void
614new_partitiontable(tptr, oldtptr)
615struct 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