installboot: fix stage2 size check for MBR
[unleashed.git] / usr / src / cmd / format / checkdev.c
blobb1d84ce1958e47a55f6284155b58bf6ae239d3a3
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
29 * This file contains miscellaneous device validation routines.
32 #include "global.h"
33 #include <sys/mnttab.h>
34 #include <sys/mntent.h>
35 #include <sys/autoconf.h>
37 #include <signal.h>
38 #include <malloc.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <libgen.h>
44 #include <sys/ioctl.h>
45 #include <sys/fcntl.h>
46 #include <sys/stat.h>
47 #include <sys/swap.h>
48 #include <sys/sysmacros.h>
49 #include <sys/mkdev.h>
50 #include <sys/modctl.h>
51 #include <ctype.h>
52 #include <libdiskmgt.h>
53 #include <libnvpair.h>
54 #include "misc.h"
55 #include "checkdev.h"
56 #include <sys/efi_partition.h>
58 /* Function prototypes */
59 #ifdef __STDC__
61 static struct swaptable *getswapentries(void);
62 static void freeswapentries(struct swaptable *);
63 static int getpartition(char *pathname);
64 static int checkpartitions(int bm_mounted);
66 #else /* __STDC__ */
68 static struct swaptable *getswapentries();
69 static void freeswapentries();
70 static int getpartition();
71 static int checkpartitions();
73 #endif /* __STDC__ */
75 extern char *getfullname();
77 static struct swaptable *
78 getswapentries(void)
80 register struct swaptable *st;
81 register struct swapent *swapent;
82 int i, num;
83 char fullpathname[MAXPATHLEN];
86 * get the number of swap entries
88 if ((num = swapctl(SC_GETNSWP, (void *)NULL)) == -1) {
89 err_print("swapctl error ");
90 fullabort();
92 if (num == 0)
93 return (NULL);
94 if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int)))
95 == NULL) {
96 err_print("getswapentries: malloc failed.\n");
97 fullabort();
99 swapent = st->swt_ent;
100 for (i = 0; i < num; i++, swapent++) {
101 if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) {
102 err_print("getswapentries: malloc failed.\n");
103 fullabort();
106 st->swt_n = num;
107 if ((num = swapctl(SC_LIST, (void *)st)) == -1) {
108 err_print("swapctl error ");
109 fullabort();
111 swapent = st->swt_ent;
112 for (i = 0; i < num; i++, swapent++) {
113 if (*swapent->ste_path != '/') {
114 (void) snprintf(fullpathname, sizeof (fullpathname),
115 "/dev/%s", swapent->ste_path);
116 (void) strcpy(swapent->ste_path, fullpathname);
119 return (st);
122 static void
123 freeswapentries(st)
124 struct swaptable *st;
126 register struct swapent *swapent;
127 int i;
129 swapent = st->swt_ent;
130 for (i = 0; i < st->swt_n; i++, swapent++)
131 free(swapent->ste_path);
132 free(st);
137 * function getpartition:
139 static int
140 getpartition(pathname)
141 char *pathname;
143 int mfd;
144 struct dk_cinfo dkinfo;
145 struct stat stbuf;
146 char raw_device[MAXPATHLEN];
147 int found = -1;
150 * Map the block device name to the raw device name.
151 * If it doesn't appear to be a device name, skip it.
153 if (match_substr(pathname, "/dev/") == 0)
154 return (found);
155 (void) strcpy(raw_device, "/dev/r");
156 (void) strcat(raw_device, pathname + strlen("/dev/"));
158 * Determine if this appears to be a disk device.
159 * First attempt to open the device. If if fails, skip it.
161 if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) {
162 return (found);
165 * Must be a character device
167 if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) {
168 (void) close(mfd);
169 return (found);
172 * Attempt to read the configuration info on the disk.
174 if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) {
175 (void) close(mfd);
176 return (found);
179 * Finished with the opened device
181 (void) close(mfd);
184 * If it's not the disk we're interested in, it doesn't apply.
186 if (cur_disk->disk_dkinfo.dki_ctype != dkinfo.dki_ctype ||
187 cur_disk->disk_dkinfo.dki_cnum != dkinfo.dki_cnum ||
188 cur_disk->disk_dkinfo.dki_unit != dkinfo.dki_unit ||
189 strcmp(cur_disk->disk_dkinfo.dki_dname,
190 dkinfo.dki_dname) != 0) {
191 return (found);
195 * Extract the partition that is mounted.
197 return (PARTITION(stbuf.st_rdev));
201 * This Routine checks to see if there are partitions used for swapping overlaps
202 * a given portion of a disk. If the start parameter is < 0, it means
203 * that the entire disk should be checked
206 checkswap(start, end)
207 diskaddr_t start, end;
209 struct swaptable *st;
210 struct swapent *swapent;
211 int i;
212 int found = 0;
213 struct dk_map32 *map;
214 int part;
217 * If we are only checking part of the disk, the disk must
218 * have a partition map to check against. If it doesn't,
219 * we hope for the best.
221 if (cur_parts == NULL)
222 return (0);
225 * check for swap entries
227 st = getswapentries();
229 * if there are no swap entries return.
231 if (st == (struct swaptable *)NULL)
232 return (0);
233 swapent = st->swt_ent;
234 for (i = 0; i < st->swt_n; i++, swapent++) {
235 if ((part = getpartition(swapent->ste_path)) != -1) {
236 if (start == UINT_MAX64) {
237 found = -1;
238 break;
240 map = &cur_parts->pinfo_map[part];
241 if ((start >= (int)(map->dkl_cylno * spc() +
242 map->dkl_nblk)) || (end < (int)(map->dkl_cylno
243 * spc()))) {
244 continue;
246 found = -1;
247 break;
250 freeswapentries(st);
252 * If we found trouble and we're running from a command file,
253 * quit before doing something we really regret.
256 if (found && option_f) {
257 err_print(
258 "Operation on disks being used for swapping must be interactive.\n");
259 cmdabort(SIGINT);
262 return (found);
267 * Determines if there are partitions that are a part of an SVM, VxVM, zpool
268 * volume or a live upgrade device, overlapping a given portion of a disk.
269 * Mounts and swap devices are checked in legacy format code.
272 checkdevinuse(char *cur_disk_path, diskaddr_t start, diskaddr_t end, int print,
273 int check_label)
276 int error;
277 int found = 0;
278 int check = 0;
279 int i;
280 int bm_inuse = 0;
281 int part = 0;
282 uint64_t slice_start, slice_size;
283 dm_descriptor_t *slices = NULL;
284 nvlist_t *attrs = NULL;
285 char *usage;
286 char *name;
289 * If the user does not want to do in use checking, return immediately.
290 * Normally, this is handled in libdiskmgt. For format, there is more
291 * processing required, so we want to bypass the in use checking
292 * here.
295 if (NOINUSE_SET)
296 return (0);
299 * Skip if it is not a real disk
301 * There could be two kinds of strings in cur_disk_path
302 * One starts with c?t?d?, while the other is a absolute path of a
303 * block device file.
306 if (*cur_disk_path != 'c') {
307 struct stat stbuf;
308 char majorname[16];
309 major_t majornum;
311 (void) stat(cur_disk_path, &stbuf);
312 majornum = major(stbuf.st_rdev);
313 (void) modctl(MODGETNAME, majorname, sizeof (majorname),
314 &majornum);
316 if (strcmp(majorname, "sd"))
317 if (strcmp(majorname, "ssd"))
318 if (strcmp(majorname, "cmdk"))
319 return (0);
323 * Truncate the characters following "d*", such as "s*" or "p*"
325 cur_disk_path = basename(cur_disk_path);
326 name = strrchr(cur_disk_path, 'd');
327 if (name) {
328 name++;
329 for (; (*name <= '9') && (*name >= '0'); name++) {
331 *name = (char)0;
336 * For format, we get basic 'in use' details from libdiskmgt. After
337 * that we must do the appropriate checking to see if the 'in use'
338 * details require a bit of additional work.
341 dm_get_slices(cur_disk_path, &slices, &error);
342 if (error) {
344 * If ENODEV, it actually means the device is not in use.
345 * We will return 0 without displaying error.
347 if (error != ENODEV) {
348 err_print("Error occurred with device in use"
349 "checking: %s\n", strerror(error));
350 return (found);
353 if (slices == NULL)
354 return (found);
356 for (i = 0; slices[i] != NULL; i++) {
358 * If we are checking the whole disk
359 * then any and all in use data is
360 * relevant.
362 if (start == UINT_MAX64) {
363 name = dm_get_name(slices[i], &error);
364 if (error != 0 || !name) {
365 err_print("Error occurred with device "
366 "in use checking: %s\n", strerror(error));
367 continue;
369 if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) ||
370 error) {
371 if (error != 0) {
372 dm_free_name(name);
373 name = NULL;
374 err_print("Error occurred with "
375 "device in use checking: "
376 "%s\n", strerror(error));
377 continue;
379 dm_free_name(name);
380 name = NULL;
382 * If this is a dump device, then it is
383 * a failure. You cannot format a slice
384 * that is a dedicated dump device.
387 if (strstr(usage, DM_USE_DUMP)) {
388 if (print) {
389 err_print(usage);
390 free(usage);
392 dm_free_descriptors(slices);
393 return (1);
396 * We really found a device that is in use.
397 * Set 'found' for the return value, and set
398 * 'check' to indicate below that we must
399 * get the partition number to set bm_inuse
400 * in the event we are trying to label this
401 * device. check_label is set when we are
402 * checking modifications for in use slices
403 * on the device.
405 found ++;
406 check = 1;
407 if (print) {
408 err_print(usage);
409 free(usage);
412 } else {
414 * Before getting the in use data, verify that the
415 * current slice is within the range we are checking.
417 attrs = dm_get_attributes(slices[i], &error);
418 if (error) {
419 err_print("Error occurred with device in use "
420 "checking: %s\n", strerror(error));
421 continue;
423 if (attrs == NULL) {
424 continue;
427 (void) nvlist_lookup_uint64(attrs, DM_START,
428 &slice_start);
429 (void) nvlist_lookup_uint64(attrs, DM_SIZE,
430 &slice_size);
431 if (start >= (slice_start + slice_size) ||
432 (end < slice_start)) {
433 nvlist_free(attrs);
434 attrs = NULL;
435 continue;
437 name = dm_get_name(slices[i], &error);
438 if (error != 0 || !name) {
439 err_print("Error occurred with device "
440 "in use checking: %s\n", strerror(error));
441 nvlist_free(attrs);
442 attrs = NULL;
443 continue;
445 if (dm_inuse(name, &usage,
446 DM_WHO_FORMAT, &error) || error) {
447 if (error != 0) {
448 dm_free_name(name);
449 name = NULL;
450 err_print("Error occurred with "
451 "device in use checking: "
452 "%s\n", strerror(error));
453 nvlist_free(attrs);
454 attrs = NULL;
455 continue;
457 dm_free_name(name);
458 name = NULL;
460 * If this is a dump device, then it is
461 * a failure. You cannot format a slice
462 * that is a dedicated dump device.
464 if (strstr(usage, DM_USE_DUMP)) {
465 if (print) {
466 err_print(usage);
467 free(usage);
469 dm_free_descriptors(slices);
470 nvlist_free(attrs);
471 return (1);
474 * We really found a device that is in use.
475 * Set 'found' for the return value, and set
476 * 'check' to indicate below that we must
477 * get the partition number to set bm_inuse
478 * in the event we are trying to label this
479 * device. check_label is set when we are
480 * checking modifications for in use slices
481 * on the device.
483 found ++;
484 check = 1;
485 if (print) {
486 err_print(usage);
487 free(usage);
492 * If check is set it means we found a slice(the current slice)
493 * on this device in use in some way. We potentially want
494 * to check this slice when labeling is
495 * requested. We set bm_inuse with this partition value
496 * for use later if check_label was set when called.
498 if (check) {
499 name = dm_get_name(slices[i], &error);
500 if (error != 0 || !name) {
501 err_print("Error occurred with device "
502 "in use checking: %s\n", strerror(error));
503 nvlist_free(attrs);
504 attrs = NULL;
505 continue;
507 part = getpartition(name);
508 dm_free_name(name);
509 name = NULL;
510 if (part != -1) {
511 bm_inuse |= 1 << part;
513 check = 0;
516 * If we have attributes then we have successfully
517 * found the slice we were looking for and we also
518 * know this means we are not searching the whole
519 * disk so break out of the loop
520 * now.
522 if (attrs) {
523 nvlist_free(attrs);
524 break;
528 if (slices) {
529 dm_free_descriptors(slices);
533 * The user is trying to label the disk. We have to do special
534 * checking here to ensure they are not trying to modify a slice
535 * that is in use in an incompatible way.
537 if (check_label && bm_inuse) {
539 * !0 indicates that we found a
540 * problem. In this case, we have overloaded
541 * the use of checkpartitions to work for
542 * in use devices. bm_inuse is representative
543 * of the slice that is in use, not that
544 * is mounted as is in the case of the normal
545 * use of checkpartitions.
547 * The call to checkpartitions will return !0 if
548 * we are trying to shrink a device that we have found
549 * to be in use above.
551 return (checkpartitions(bm_inuse));
554 return (found);
557 * This routine checks to see if there are mounted partitions overlapping
558 * a given portion of a disk. If the start parameter is < 0, it means
559 * that the entire disk should be checked.
562 checkmount(start, end)
563 diskaddr_t start, end;
565 FILE *fp;
566 int found = 0;
567 struct dk_map32 *map;
568 int part;
569 struct mnttab mnt_record;
570 struct mnttab *mp = &mnt_record;
573 * If we are only checking part of the disk, the disk must
574 * have a partition map to check against. If it doesn't,
575 * we hope for the best.
577 if (cur_parts == NULL)
578 return (0);
581 * Lock out interrupts because of the mntent protocol.
583 enter_critical();
585 * Open the mount table.
587 fp = fopen(MNTTAB, "r");
588 if (fp == NULL) {
589 err_print("Unable to open mount table.\n");
590 fullabort();
593 * Loop through the mount table until we run out of entries.
595 while ((getmntent(fp, mp)) != -1) {
597 if ((part = getpartition(mp->mnt_special)) == -1)
598 continue;
601 * It's a mount on the disk we're checking. If we are
602 * checking whole disk, then we found trouble. We can
603 * quit searching.
605 if (start == UINT_MAX64) {
606 found = -1;
607 break;
611 * If the partition overlaps the zone we're checking,
612 * then we found trouble. We can quit searching.
614 map = &cur_parts->pinfo_map[part];
615 if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) ||
616 (end < (int)(map->dkl_cylno * spc()))) {
617 continue;
619 found = -1;
620 break;
623 * Close down the mount table.
625 (void) fclose(fp);
626 exit_critical();
629 * If we found trouble and we're running from a command file,
630 * quit before doing something we really regret.
633 if (found && option_f) {
634 err_print("Operation on mounted disks must be interactive.\n");
635 cmdabort(SIGINT);
638 * Return the result.
640 return (found);
644 check_label_with_swap()
646 int i;
647 struct swaptable *st;
648 struct swapent *swapent;
649 int part;
650 int bm_swap = 0;
653 * If we are only checking part of the disk, the disk must
654 * have a partition map to check against. If it doesn't,
655 * we hope for the best.
657 if (cur_parts == NULL)
658 return (0); /* Will be checked later */
661 * Check for swap entries
663 st = getswapentries();
665 * if there are no swap entries return.
667 if (st == (struct swaptable *)NULL)
668 return (0);
669 swapent = st->swt_ent;
670 for (i = 0; i < st->swt_n; i++, swapent++)
671 if ((part = getpartition(swapent->ste_path)) != -1)
672 bm_swap |= (1 << part);
673 freeswapentries(st);
675 return (checkpartitions(bm_swap));
679 * Check the new label with the existing label on the disk,
680 * to make sure that any mounted partitions are not being
681 * affected by writing the new label.
684 check_label_with_mount()
686 FILE *fp;
687 int part;
688 struct mnttab mnt_record;
689 struct mnttab *mp = &mnt_record;
690 int bm_mounted = 0;
694 * If we are only checking part of the disk, the disk must
695 * have a partition map to check against. If it doesn't,
696 * we hope for the best.
698 if (cur_parts == NULL)
699 return (0); /* Will be checked later */
702 * Lock out interrupts because of the mntent protocol.
704 enter_critical();
706 * Open the mount table.
708 fp = fopen(MNTTAB, "r");
709 if (fp == NULL) {
710 err_print("Unable to open mount table.\n");
711 fullabort();
714 * Loop through the mount table until we run out of entries.
716 while ((getmntent(fp, mp)) != -1) {
717 if ((part = getpartition(mp->mnt_special)) != -1)
718 bm_mounted |= (1 << part);
721 * Close down the mount table.
723 (void) fclose(fp);
724 exit_critical();
726 return (checkpartitions(bm_mounted));
731 * This Routine checks if any partitions specified
732 * are affected by writing the new label
734 static int
735 checkpartitions(int bm_mounted)
737 struct dk_map32 *n;
738 struct dk_map *o;
739 struct dk_allmap old_map;
740 int i, found = 0;
741 struct partition64 o_efi;
744 * Now we need to check that the current partition list and the
745 * previous partition list (which there must be if we actually
746 * have partitions mounted) overlap in any way on the mounted
747 * partitions
751 * Check if the user wants to online-label an
752 * existing EFI label.
754 if (cur_label == L_TYPE_EFI) {
755 for (i = 0; i < EFI_NUMPAR; i++) {
756 if (bm_mounted & (1 << i)) {
757 o_efi.p_partno = i;
758 if (ioctl(cur_file, DKIOCPARTITION, &o_efi)
759 == -1) {
760 err_print("Unable to get information "
761 "for EFI partition %d.\n", i);
762 return (-1);
766 * Partition can grow or remain same.
768 if (o_efi.p_start == cur_parts->etoc->
769 efi_parts[i].p_start && o_efi.p_size
770 <= cur_parts->etoc->efi_parts[i].p_size) {
771 continue;
774 found = -1;
776 if (found)
777 break;
780 } else {
783 * Get the "real" (on-disk) version of the partition table
785 if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) {
786 err_print("Unable to get current partition map.\n");
787 return (-1);
789 for (i = 0; i < NDKMAP; i++) {
790 if (bm_mounted & (1 << i)) {
792 * This partition is mounted
794 o = &old_map.dka_map[i];
795 n = &cur_parts->pinfo_map[i];
796 #ifdef DEBUG
797 fmt_print(
798 "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE);
799 #endif
801 * If partition is identical, we're fine.
802 * If the partition grows, we're also fine,
803 * because the routines in partition.c check
804 * for overflow. It will (ultimately) be up
805 * to the routines in partition.c to warn
806 * about creation of overlapping partitions.
808 if (o->dkl_cylno == n->dkl_cylno &&
809 o->dkl_nblk <= n->dkl_nblk) {
810 #ifdef DEBUG
811 if (o->dkl_nblk < n->dkl_nblk) {
812 fmt_print(
813 "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk);
815 fmt_print("\n");
816 #endif
817 continue;
819 #ifdef DEBUG
820 fmt_print("- changes; old (%d,%d)->new "
821 "(%d,%d)\n", o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, n->dkl_nblk);
822 #endif
823 found = -1;
825 if (found)
826 break;
831 * If we found trouble and we're running from a command file,
832 * quit before doing something we really regret.
835 if (found && option_f) {
836 err_print("Operation on mounted disks or \
837 disks currently being used for swapping must be interactive.\n");
838 cmdabort(SIGINT);
841 * Return the result.
843 return (found);