xref: /illumos-gate/usr/src/cmd/format/partition.c (revision b12aaafb)
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  * Copyright (c) 2016 by Delphix. All rights reserved.
25  */
26 
27 /*
28  * This file contains functions that operate on partition tables.
29  */
30 #include <string.h>
31 #include <stdlib.h>
32 #include "global.h"
33 #include "partition.h"
34 #include "misc.h"
35 #include "menu_command.h"
36 #include "menu_partition.h"
37 
38 
39 /*
40  * Default vtoc information for non-SVr4 partitions
41  */
42 struct dk_map2	default_vtoc_map[NDKMAP] = {
43 	{	V_ROOT,		0	},		/* a - 0 */
44 	{	V_SWAP,		V_UNMNT	},		/* b - 1 */
45 	{	V_BACKUP,	V_UNMNT	},		/* c - 2 */
46 	{	V_UNASSIGNED,	0	},		/* d - 3 */
47 	{	V_UNASSIGNED,	0	},		/* e - 4 */
48 	{	V_UNASSIGNED,	0	},		/* f - 5 */
49 	{	V_USR,		0	},		/* g - 6 */
50 	{	V_UNASSIGNED,	0	},		/* h - 7 */
51 
52 #if defined(_SUNOS_VTOC_16)
53 
54 #if defined(i386)
55 	{	V_BOOT,		V_UNMNT	},		/* i - 8 */
56 	{	V_ALTSCTR,	0	},		/* j - 9 */
57 
58 #else
59 #error No VTOC format defined.
60 #endif			/* defined(i386) */
61 
62 	{	V_UNASSIGNED,	0	},		/* k - 10 */
63 	{	V_UNASSIGNED,	0	},		/* l - 11 */
64 	{	V_UNASSIGNED,	0	},		/* m - 12 */
65 	{	V_UNASSIGNED,	0	},		/* n - 13 */
66 	{	V_UNASSIGNED,	0	},		/* o - 14 */
67 	{	V_UNASSIGNED,	0	},		/* p - 15 */
68 #endif			/* defined(_SUNOS_VTOC_16) */
69 };
70 
71 /*
72  * This routine finds the last usable sector in the partition table.
73  * It skips the BACKUP partition.
74  */
75 static uint64_t
maxofN(struct dk_gpt * map)76 maxofN(struct dk_gpt *map)
77 {
78 	uint64_t	max;
79 	uint64_t	sec_no[2], start[2], size[2];
80 	int		i;
81 
82 	for (i = 0; i < map->efi_nparts - 1; i++) {
83 		start[0] = map->efi_parts[i].p_start;
84 		size[0] = map->efi_parts[i].p_size;
85 		sec_no[0] = start[0] + size[0];
86 
87 		start[1] = map->efi_parts[i + 1].p_start;
88 		size[1] = map->efi_parts[i + 1].p_size;
89 		sec_no[1] = start[1] + size[1];
90 
91 		if (map->efi_parts[i].p_tag == V_BACKUP) {
92 			sec_no[0] = 0;
93 		}
94 		if (map->efi_parts[i+1].p_tag == V_BACKUP) {
95 			sec_no[1] = 0;
96 		}
97 		if (i == 0) {
98 			max = sec_no[1];
99 		}
100 		if (sec_no[0] > max) {
101 			max = sec_no[0];
102 		} else {
103 			max = max;
104 		}
105 	}
106 	if (max == 0)
107 		max = map->efi_first_u_lba;
108 	return (max);
109 }
110 
111 /*
112  * This routine allows the user to change the boundaries of the given
113  * partition in the current partition map.
114  */
115 void
change_partition(int num)116 change_partition(int num)
117 {
118 	uint_t		i;
119 	uint64_t	i64, j64;
120 	uint_t		j;
121 	int		deflt;
122 	part_deflt_t	p_deflt;
123 	u_ioparam_t	ioparam;
124 	int		tag;
125 	int		flag;
126 	char		msg[256];
127 	blkaddr32_t	cyl_offset = 0;
128 	efi_deflt_t	efi_deflt;
129 
130 	/*
131 	 * check if there exists a partition table for the disk.
132 	 */
133 	if (cur_parts == NULL) {
134 		err_print("Current Disk has no partition table.\n");
135 		return;
136 	}
137 
138 	if (cur_label == L_TYPE_EFI) {
139 		if (num > cur_parts->etoc->efi_nparts - 1) {
140 			err_print("Invalid partition for EFI label\n");
141 			return;
142 		}
143 		print_efi_partition(cur_parts->etoc, num, 1);
144 		fmt_print("\n");
145 		/*
146 		 * Prompt for p_tag and p_flag values for this partition
147 		 */
148 		deflt = cur_parts->etoc->efi_parts[num].p_tag;
149 		if (deflt == V_UNASSIGNED) {
150 			deflt = V_USR;
151 		}
152 		(void) sprintf(msg, "Enter partition id tag");
153 		ioparam.io_slist = ptag_choices;
154 		tag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT);
155 
156 		deflt = cur_parts->etoc->efi_parts[num].p_flag;
157 		(void) sprintf(msg, "Enter partition permission flags");
158 		ioparam.io_slist = pflag_choices;
159 		flag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT);
160 
161 		ioparam.io_bounds.lower = cur_parts->etoc->efi_first_u_lba;
162 		ioparam.io_bounds.upper = cur_parts->etoc->efi_last_u_lba;
163 
164 		efi_deflt.start_sector = maxofN(cur_parts->etoc);
165 		if ((cur_parts->etoc->efi_parts[num].p_start != 0) &&
166 		    (cur_parts->etoc->efi_parts[num].p_size != 0)) {
167 			efi_deflt.start_sector =
168 			    cur_parts->etoc->efi_parts[num].p_start;
169 		}
170 		efi_deflt.end_sector = ioparam.io_bounds.upper -
171 		    efi_deflt.start_sector;
172 		i64 = input(FIO_INT64, "Enter new starting Sector", ':',
173 		    &ioparam, (int *)&efi_deflt, DATA_INPUT);
174 
175 		ioparam.io_bounds.lower = 0;
176 		ioparam.io_bounds.upper = cur_parts->etoc->efi_last_u_lba;
177 		efi_deflt.end_sector = cur_parts->etoc->efi_parts[num].p_size;
178 		efi_deflt.start_sector = i64;
179 		j64 = input(FIO_EFI, "Enter partition size", ':', &ioparam,
180 		    (int *)&efi_deflt, DATA_INPUT);
181 		if (j64 == 0) {
182 			tag = V_UNASSIGNED;
183 			i64 = 0;
184 		} else if ((j64 != 0) && (tag == V_UNASSIGNED)) {
185 			tag = V_USR;
186 		}
187 
188 		if (cur_parts->pinfo_name != NULL)
189 			make_partition();
190 
191 		cur_parts->etoc->efi_parts[num].p_tag = tag;
192 		cur_parts->etoc->efi_parts[num].p_flag = flag;
193 		cur_parts->etoc->efi_parts[num].p_start = i64;
194 		cur_parts->etoc->efi_parts[num].p_size = j64;
195 		/*
196 		 * We are now done with EFI part, so return now
197 		 */
198 		return;
199 	}
200 	/*
201 	 * Print out the given partition so the user knows what they're
202 	 * getting into.
203 	 */
204 	print_partition(cur_parts, num, 1);
205 	fmt_print("\n");
206 
207 	/*
208 	 * Prompt for p_tag and p_flag values for this partition.
209 	 */
210 	assert(cur_parts->vtoc.v_version == V_VERSION);
211 	deflt = cur_parts->vtoc.v_part[num].p_tag;
212 	(void) sprintf(msg, "Enter partition id tag");
213 	ioparam.io_slist = ptag_choices;
214 	tag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT);
215 
216 	deflt = cur_parts->vtoc.v_part[num].p_flag;
217 	(void) sprintf(msg, "Enter partition permission flags");
218 	ioparam.io_slist = pflag_choices;
219 	flag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT);
220 
221 	/*
222 	 * Ask for the new values.  The old values are the defaults, and
223 	 * strict bounds checking is done on the values given.
224 	 */
225 
226 #if defined(i386)
227 
228 	if (tag != V_UNASSIGNED && tag != V_BACKUP && tag != V_BOOT) {
229 		/*
230 		 * Determine cyl offset for boot and alternate partitions.
231 		 * Assuming that the alternate sectors partition (slice)
232 		 * physical location immediately follows the boot
233 		 * partition and partition sizes are expressed in multiples
234 		 * of cylinder size.
235 		 */
236 		cyl_offset = cur_parts->pinfo_map[I_PARTITION].dkl_cylno + 1;
237 		if (tag != V_ALTSCTR) {
238 			if (cur_parts->pinfo_map[J_PARTITION].dkl_nblk != 0) {
239 				cyl_offset =
240 				    cur_parts->
241 				    pinfo_map[J_PARTITION].dkl_cylno +
242 				    ((cur_parts->
243 				    pinfo_map[J_PARTITION].dkl_nblk +
244 				    (spc() - 1)) / spc());
245 			}
246 		}
247 	}
248 #endif	/* defined(i386) */
249 
250 	ioparam.io_bounds.lower = 0;
251 	ioparam.io_bounds.upper = ncyl - 1;
252 	deflt = max(cur_parts->pinfo_map[num].dkl_cylno, cyl_offset);
253 	i = (uint_t)input(FIO_INT, "Enter new starting cyl", ':', &ioparam,
254 	    &deflt, DATA_INPUT);
255 
256 	ioparam.io_bounds.lower = 0;
257 	ioparam.io_bounds.upper = (ncyl - i) * spc();
258 
259 	/* fill in defaults for the current partition */
260 	p_deflt.start_cyl = i;
261 	p_deflt.deflt_size = min(cur_parts->pinfo_map[num].dkl_nblk,
262 	    ioparam.io_bounds.upper);
263 
264 	/* call input, passing p_deflt's address, typecast to (int *) */
265 	j = (uint_t)input(FIO_ECYL, "Enter partition size", ':', &ioparam,
266 	    (int *)&p_deflt, DATA_INPUT);
267 
268 	/*
269 	 * If the current partition has a size of zero change the
270 	 * tag to Unassigned and the starting cylinder to zero
271 	 */
272 
273 	if (j == 0) {
274 		tag = V_UNASSIGNED;
275 		i = 0;
276 	}
277 
278 
279 #if defined(i386)
280 
281 	if (i < cyl_offset && tag != V_UNASSIGNED && tag != V_BACKUP &&
282 	    tag != V_BOOT) {
283 		/*
284 		 * This slice overlaps boot and/or alternates slice
285 		 * Check if it's the boot or alternates slice and warn
286 		 * accordingly
287 		 */
288 		if (i < cur_parts->pinfo_map[I_PARTITION].dkl_cylno + 1) {
289 			fmt_print("\nWarning: Partition overlaps boot ");
290 			fmt_print("partition. Specify different start cyl.\n");
291 			return;
292 		}
293 		/*
294 		 * Cyl offset for alternates partition was calculated before
295 		 */
296 		if (i < cyl_offset) {
297 			fmt_print("\nWarning: Partition overlaps alternates ");
298 			fmt_print("partition. Specify different start cyl.\n");
299 			return;
300 		}
301 	}
302 
303 #endif	/* defined(i386) */
304 
305 	/*
306 	 * If user has entered a V_BACKUP tag then the partition
307 	 * size should specify full disk capacity else
308 	 * return an Error.
309 	 */
310 	if (tag == V_BACKUP) {
311 		uint_t fullsz;
312 
313 		fullsz = ncyl * nhead * nsect;
314 		if (fullsz != j) {
315 		/*
316 		 * V_BACKUP Tag Partition != full disk capacity.
317 		 * print useful messages.
318 		 */
319 		fmt_print("\nWarning: Partition with V_BACKUP tag should ");
320 		fmt_print("specify full disk capacity. \n");
321 		return;
322 		}
323 	}
324 
325 
326 	/*
327 	 * If the current partition is named, we can't change it.
328 	 * We create a new current partition map instead.
329 	 */
330 	if (cur_parts->pinfo_name != NULL)
331 		make_partition();
332 	/*
333 	 * Change the values.
334 	 */
335 	cur_parts->pinfo_map[num].dkl_cylno = i;
336 	cur_parts->pinfo_map[num].dkl_nblk = j;
337 
338 #if defined(_SUNOS_VTOC_16)
339 	cur_parts->vtoc.v_part[num].p_start = (daddr_t)(i * (nhead * nsect));
340 	cur_parts->vtoc.v_part[num].p_size = (long)j;
341 #endif	/* defined(_SUNOS_VTOC_16) */
342 
343 	/*
344 	 * Install the p_tag and p_flag values for this partition
345 	 */
346 	assert(cur_parts->vtoc.v_version == V_VERSION);
347 	cur_parts->vtoc.v_part[num].p_tag = (ushort_t)tag;
348 	cur_parts->vtoc.v_part[num].p_flag = (ushort_t)flag;
349 }
350 
351 
352 /*
353  * This routine picks to closest partition table which matches the
354  * selected disk type.  It is called each time the disk type is
355  * changed.  If no match is found, it uses the first element
356  * of the partition table.  If no table exists, a dummy is
357  * created.
358  */
359 int
get_partition(void)360 get_partition(void)
361 {
362 	register struct partition_info *pptr;
363 	register struct partition_info *parts;
364 
365 	/*
366 	 * If there are no pre-defined maps for this disk type, it's
367 	 * an error.
368 	 */
369 	parts = cur_dtype->dtype_plist;
370 	if (parts == NULL) {
371 		err_print("No defined partition tables.\n");
372 		make_partition();
373 		return (-1);
374 	}
375 	/*
376 	 * Loop through the pre-defined maps searching for one which match
377 	 * disk type.  If found copy it into unmamed partition.
378 	 */
379 	enter_critical();
380 	for (pptr = parts; pptr != NULL; pptr = pptr->pinfo_next) {
381 		if (cur_dtype->dtype_asciilabel) {
382 			if (pptr->pinfo_name != NULL && strcmp(pptr->pinfo_name,
383 			    cur_dtype->dtype_asciilabel) == 0) {
384 				/*
385 				 * Set current partition and name it.
386 				 */
387 				cur_disk->disk_parts = cur_parts = pptr;
388 				cur_parts->pinfo_name = pptr->pinfo_name;
389 				exit_critical();
390 				return (0);
391 			}
392 		}
393 	}
394 	/*
395 	 * If we couldn't find a match, take the first one.
396 	 * Set current partition and name it.
397 	 */
398 	cur_disk->disk_parts = cur_parts = cur_dtype->dtype_plist;
399 	cur_parts->pinfo_name = parts->pinfo_name;
400 	exit_critical();
401 	return (0);
402 }
403 
404 
405 /*
406  * This routine creates a new partition map and sets it current.  If there
407  * was a current map, the new map starts out identical to it.  Otherwise
408  * the new map starts out all zeroes.
409  */
410 void
make_partition(void)411 make_partition(void)
412 {
413 	register struct partition_info *pptr, *parts;
414 	int	i;
415 
416 	/*
417 	 * Lock out interrupts so the lists don't get mangled.
418 	 */
419 	enter_critical();
420 	/*
421 	 * Get space for for the new map and link it into the list
422 	 * of maps for the current disk type.
423 	 */
424 	pptr = (struct partition_info *)zalloc(sizeof (struct partition_info));
425 	parts = cur_dtype->dtype_plist;
426 	if (parts == NULL) {
427 		cur_dtype->dtype_plist = pptr;
428 	} else {
429 		while (parts->pinfo_next != NULL) {
430 			parts = parts->pinfo_next;
431 		}
432 		parts->pinfo_next = pptr;
433 		pptr->pinfo_next = NULL;
434 	}
435 	/*
436 	 * If there was a current map, copy its values.
437 	 */
438 	if (cur_label == L_TYPE_EFI) {
439 		struct dk_gpt	*map;
440 		int		nparts;
441 		int		size;
442 
443 		nparts = cur_parts->etoc->efi_nparts;
444 		size = sizeof (struct dk_part) * nparts +
445 		    sizeof (struct dk_gpt);
446 		map = zalloc(size);
447 		(void) memcpy(map, cur_parts->etoc, size);
448 		pptr->etoc = map;
449 		cur_disk->disk_parts = cur_parts = pptr;
450 		exit_critical();
451 		return;
452 	}
453 	if (cur_parts != NULL) {
454 		for (i = 0; i < NDKMAP; i++) {
455 			pptr->pinfo_map[i] = cur_parts->pinfo_map[i];
456 		}
457 		pptr->vtoc = cur_parts->vtoc;
458 	} else {
459 		/*
460 		 * Otherwise set initial default vtoc values
461 		 */
462 		set_vtoc_defaults(pptr);
463 	}
464 
465 	/*
466 	 * Make the new one current.
467 	 */
468 	cur_disk->disk_parts = cur_parts = pptr;
469 	exit_critical();
470 }
471 
472 
473 /*
474  * This routine deletes a partition map from the list of maps for
475  * the given disk type.
476  */
477 void
delete_partition(struct partition_info * parts)478 delete_partition(struct partition_info *parts)
479 {
480 	struct	partition_info *pptr;
481 
482 	/*
483 	 * If there isn't a current map, it's an error.
484 	 */
485 	if (cur_dtype->dtype_plist == NULL) {
486 		err_print("Error: unexpected null partition list.\n");
487 		fullabort();
488 	}
489 	/*
490 	 * Remove the map from the list.
491 	 */
492 	if (cur_dtype->dtype_plist == parts)
493 		cur_dtype->dtype_plist = parts->pinfo_next;
494 	else {
495 		for (pptr = cur_dtype->dtype_plist; pptr->pinfo_next != parts;
496 		    pptr = pptr->pinfo_next)
497 			;
498 		pptr->pinfo_next = parts->pinfo_next;
499 	}
500 	/*
501 	 * Free the space it was using.
502 	 */
503 	destroy_data((char *)parts);
504 }
505 
506 
507 /*
508  * Set all partition vtoc fields to defaults
509  */
510 void
set_vtoc_defaults(struct partition_info * part)511 set_vtoc_defaults(struct partition_info *part)
512 {
513 	int	i;
514 
515 	bzero((caddr_t)&part->vtoc, sizeof (struct dk_vtoc));
516 
517 	part->vtoc.v_version = V_VERSION;
518 	part->vtoc.v_nparts = NDKMAP;
519 	part->vtoc.v_sanity = VTOC_SANE;
520 
521 	for (i = 0; i < NDKMAP; i++) {
522 		part->vtoc.v_part[i].p_tag = default_vtoc_map[i].p_tag;
523 		part->vtoc.v_part[i].p_flag = default_vtoc_map[i].p_flag;
524 	}
525 }
526