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