xref: /illumos-gate/usr/src/cmd/format/partition.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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
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 = 34;
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
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 = 34;
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", ':', &ioparam,
173 		(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->pinfo_map[J_PARTITION].dkl_cylno +
241 				((cur_parts->pinfo_map[J_PARTITION].dkl_nblk +
242 				(spc()-1)) / spc());
243 			}
244 		}
245 	}
246 #endif	/* defined(i386) */
247 
248 	ioparam.io_bounds.lower = 0;
249 	ioparam.io_bounds.upper = ncyl - 1;
250 	deflt = max(cur_parts->pinfo_map[num].dkl_cylno,
251 		cyl_offset);
252 	i = (uint_t)input(FIO_INT, "Enter new starting cyl", ':', &ioparam,
253 	    &deflt, DATA_INPUT);
254 
255 	ioparam.io_bounds.lower = 0;
256 	ioparam.io_bounds.upper = (ncyl - i) * spc();
257 
258 	/* fill in defaults for the current partition */
259 	p_deflt.start_cyl = i;
260 	p_deflt.deflt_size =
261 		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
360 get_partition()
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
411 make_partition()
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 + sizeof (struct dk_gpt);
445 	    map = zalloc(size);
446 	    (void) memcpy(map, cur_parts->etoc, size);
447 	    pptr->etoc = map;
448 	    cur_disk->disk_parts = cur_parts = pptr;
449 	    exit_critical();
450 	    return;
451 	}
452 	if (cur_parts != NULL) {
453 		for (i = 0; i < NDKMAP; i++) {
454 			pptr->pinfo_map[i] = cur_parts->pinfo_map[i];
455 		}
456 		pptr->vtoc = cur_parts->vtoc;
457 	} else {
458 		/*
459 		 * Otherwise set initial default vtoc values
460 		 */
461 		set_vtoc_defaults(pptr);
462 	}
463 
464 	/*
465 	 * Make the new one current.
466 	 */
467 	cur_disk->disk_parts = cur_parts = pptr;
468 	exit_critical();
469 }
470 
471 
472 /*
473  * This routine deletes a partition map from the list of maps for
474  * the given disk type.
475  */
476 void
477 delete_partition(struct partition_info *parts)
478 {
479 	struct	partition_info *pptr;
480 
481 	/*
482 	 * If there isn't a current map, it's an error.
483 	 */
484 	if (cur_dtype->dtype_plist == NULL) {
485 		err_print("Error: unexpected null partition list.\n");
486 		fullabort();
487 	}
488 	/*
489 	 * Remove the map from the list.
490 	 */
491 	if (cur_dtype->dtype_plist == parts)
492 		cur_dtype->dtype_plist = parts->pinfo_next;
493 	else {
494 		for (pptr = cur_dtype->dtype_plist; pptr->pinfo_next != parts;
495 		    pptr = pptr->pinfo_next)
496 			;
497 		pptr->pinfo_next = parts->pinfo_next;
498 	}
499 	/*
500 	 * Free the space it was using.
501 	 */
502 	destroy_data((char *)parts);
503 }
504 
505 
506 /*
507  * Set all partition vtoc fields to defaults
508  */
509 void
510 set_vtoc_defaults(struct partition_info *part)
511 {
512 	int	i;
513 
514 	bzero((caddr_t)&part->vtoc, sizeof (struct dk_vtoc));
515 
516 	part->vtoc.v_version = V_VERSION;
517 	part->vtoc.v_nparts = NDKMAP;
518 	part->vtoc.v_sanity = VTOC_SANE;
519 
520 	for (i = 0; i < NDKMAP; i++) {
521 		part->vtoc.v_part[i].p_tag = default_vtoc_map[i].p_tag;
522 		part->vtoc.v_part[i].p_flag = default_vtoc_map[i].p_flag;
523 	}
524 }
525