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