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