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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27
28 /*
29 * This file contains miscellaneous device validation routines.
30 */
31
32 #include "global.h"
33 #include <sys/mnttab.h>
34 #include <sys/mntent.h>
35 #include <sys/autoconf.h>
36
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>
57
58 /* Function prototypes */
59 static struct swaptable *getswapentries(void);
60 static void freeswapentries(struct swaptable *);
61 static int getpartition(char *pathname);
62 static int checkpartitions(int bm_mounted);
63
64 static struct swaptable *
getswapentries(void)65 getswapentries(void)
66 {
67 struct swaptable *st;
68 struct swapent *swapent;
69 int i, num;
70 char fullpathname[MAXPATHLEN];
71
72 /*
73 * get the number of swap entries
74 */
75 if ((num = swapctl(SC_GETNSWP, NULL)) == -1) {
76 err_print("swapctl error ");
77 fullabort();
78 }
79 if (num == 0)
80 return (NULL);
81 if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int)))
82 == NULL) {
83 err_print("getswapentries: malloc failed.\n");
84 fullabort();
85 }
86 swapent = st->swt_ent;
87 for (i = 0; i < num; i++, swapent++) {
88 if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) {
89 err_print("getswapentries: malloc failed.\n");
90 fullabort();
91 }
92 }
93 st->swt_n = num;
94 if ((num = swapctl(SC_LIST, (void *)st)) == -1) {
95 err_print("swapctl error ");
96 fullabort();
97 }
98 swapent = st->swt_ent;
99 for (i = 0; i < num; i++, swapent++) {
100 if (*swapent->ste_path != '/') {
101 (void) snprintf(fullpathname, sizeof (fullpathname),
102 "/dev/%s", swapent->ste_path);
103 (void) strcpy(swapent->ste_path, fullpathname);
104 }
105 }
106 return (st);
107 }
108
109 static void
freeswapentries(struct swaptable * st)110 freeswapentries(struct swaptable *st)
111 {
112 struct swapent *swapent;
113 int i;
114
115 swapent = st->swt_ent;
116 for (i = 0; i < st->swt_n; i++, swapent++)
117 free(swapent->ste_path);
118 free(st);
119
120 }
121
122 /*
123 * function getpartition:
124 */
125 static int
getpartition(char * pathname)126 getpartition(char *pathname)
127 {
128 int mfd;
129 struct dk_cinfo dkinfo;
130 struct stat stbuf;
131 char raw_device[MAXPATHLEN];
132 int found = -1;
133
134 /*
135 * Map the block device name to the raw device name.
136 * If it doesn't appear to be a device name, skip it.
137 */
138 if (match_substr(pathname, "/dev/") == 0)
139 return (found);
140 (void) strcpy(raw_device, "/dev/r");
141 (void) strcat(raw_device, pathname + strlen("/dev/"));
142 /*
143 * Determine if this appears to be a disk device.
144 * First attempt to open the device. If if fails, skip it.
145 */
146 if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) {
147 return (found);
148 }
149 /*
150 * Must be a character device
151 */
152 if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) {
153 (void) close(mfd);
154 return (found);
155 }
156 /*
157 * Attempt to read the configuration info on the disk.
158 */
159 if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) {
160 (void) close(mfd);
161 return (found);
162 }
163 /*
164 * Finished with the opened device
165 */
166 (void) close(mfd);
167
168 /*
169 * If it's not the disk we're interested in, it doesn't apply.
170 */
171 if (cur_disk->disk_dkinfo.dki_ctype != dkinfo.dki_ctype ||
172 cur_disk->disk_dkinfo.dki_cnum != dkinfo.dki_cnum ||
173 cur_disk->disk_dkinfo.dki_unit != dkinfo.dki_unit ||
174 strcmp(cur_disk->disk_dkinfo.dki_dname, dkinfo.dki_dname) != 0) {
175 return (found);
176 }
177
178 /*
179 * Extract the partition that is mounted.
180 */
181 return (PARTITION(stbuf.st_rdev));
182 }
183
184 /*
185 * This Routine checks to see if there are partitions used for swapping overlaps
186 * a given portion of a disk. If the start parameter is < 0, it means
187 * that the entire disk should be checked
188 */
189 int
checkswap(diskaddr_t start,diskaddr_t end)190 checkswap(diskaddr_t start, diskaddr_t end)
191 {
192 struct swaptable *st;
193 struct swapent *swapent;
194 int i;
195 int found = 0;
196 struct dk_map32 *map;
197 int part;
198
199 /*
200 * If we are only checking part of the disk, the disk must
201 * have a partition map to check against. If it doesn't,
202 * we hope for the best.
203 */
204 if (cur_parts == NULL)
205 return (0);
206
207 /*
208 * check for swap entries
209 */
210 st = getswapentries();
211 /*
212 * if there are no swap entries return.
213 */
214 if (st == NULL)
215 return (0);
216 swapent = st->swt_ent;
217 for (i = 0; i < st->swt_n; i++, swapent++) {
218 if ((part = getpartition(swapent->ste_path)) != -1) {
219 if (start == UINT_MAX64) {
220 found = -1;
221 break;
222 }
223 map = &cur_parts->pinfo_map[part];
224 if ((start >= (int)(map->dkl_cylno * spc() +
225 map->dkl_nblk)) ||
226 (end < (int)(map->dkl_cylno * spc()))) {
227 continue;
228 }
229 found = -1;
230 break;
231 };
232 }
233 freeswapentries(st);
234 /*
235 * If we found trouble and we're running from a command file,
236 * quit before doing something we really regret.
237 */
238
239 if (found && option_f) {
240 err_print(
241 "Operation on disks being used for swapping must be interactive.\n");
242 cmdabort(SIGINT);
243 }
244
245 return (found);
246
247
248 }
249 /*
250 * Determines if there are partitions that are a part of an SVM, VxVM, zpool
251 * volume or a live upgrade device, overlapping a given portion of a disk.
252 * Mounts and swap devices are checked in legacy format code.
253 */
254 int
checkdevinuse(char * cur_disk_path,diskaddr_t start,diskaddr_t end,int print,int check_label)255 checkdevinuse(char *cur_disk_path, diskaddr_t start, diskaddr_t end, int print,
256 int check_label)
257 {
258
259 int error;
260 int found = 0;
261 int check = 0;
262 int i;
263 int bm_inuse = 0;
264 int part = 0;
265 uint64_t slice_start, slice_size;
266 dm_descriptor_t *slices = NULL;
267 nvlist_t *attrs = NULL;
268 char *usage;
269 char *name;
270
271 /*
272 * If the user does not want to do in use checking, return immediately.
273 * Normally, this is handled in libdiskmgt. For format, there is more
274 * processing required, so we want to bypass the in use checking
275 * here.
276 */
277
278 if (NOINUSE_SET)
279 return (0);
280
281 /*
282 * Skip if it is not a real disk
283 *
284 * There could be two kinds of strings in cur_disk_path
285 * One starts with c?t?d?, while the other is a absolute path of a
286 * block device file.
287 */
288
289 if (*cur_disk_path != 'c') {
290 struct stat stbuf;
291 char majorname[16];
292 major_t majornum;
293
294 (void) stat(cur_disk_path, &stbuf);
295 majornum = major(stbuf.st_rdev);
296 (void) modctl(MODGETNAME, majorname, sizeof (majorname),
297 &majornum);
298
299 if (strcmp(majorname, "sd"))
300 if (strcmp(majorname, "ssd"))
301 if (strcmp(majorname, "cmdk"))
302 return (0);
303 }
304
305 /*
306 * Truncate the characters following "d*", such as "s*" or "p*"
307 */
308 cur_disk_path = basename(cur_disk_path);
309 name = strrchr(cur_disk_path, 'd');
310 if (name) {
311 name++;
312 for (; (*name <= '9') && (*name >= '0'); name++) {
313 }
314 *name = (char)0;
315 }
316
317
318 /*
319 * For format, we get basic 'in use' details from libdiskmgt. After
320 * that we must do the appropriate checking to see if the 'in use'
321 * details require a bit of additional work.
322 */
323
324 dm_get_slices(cur_disk_path, &slices, &error);
325 if (error) {
326 /*
327 * If ENODEV, it actually means the device is not in use.
328 * We will return 0 without displaying error.
329 */
330 if (error != ENODEV) {
331 err_print("Error occurred with device in use"
332 "checking: %s\n", strerror(error));
333 return (found);
334 }
335 }
336 if (slices == NULL)
337 return (found);
338
339 for (i = 0; slices[i] != 0; i++) {
340 /*
341 * If we are checking the whole disk
342 * then any and all in use data is
343 * relevant.
344 */
345 if (start == UINT_MAX64) {
346 name = dm_get_name(slices[i], &error);
347 if (error != 0 || !name) {
348 err_print("Error occurred with device "
349 "in use checking: %s\n", strerror(error));
350 continue;
351 }
352 if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) ||
353 error) {
354 if (error != 0) {
355 dm_free_name(name);
356 name = NULL;
357 err_print("Error occurred with "
358 "device in use checking: "
359 "%s\n", strerror(error));
360 continue;
361 }
362 dm_free_name(name);
363 name = NULL;
364 /*
365 * If this is a dump device, then it is
366 * a failure. You cannot format a slice
367 * that is a dedicated dump device.
368 */
369
370 if (strstr(usage, DM_USE_DUMP)) {
371 if (print) {
372 err_print(usage);
373 free(usage);
374 }
375 dm_free_descriptors(slices);
376 return (1);
377 }
378 /*
379 * We really found a device that is in use.
380 * Set 'found' for the return value, and set
381 * 'check' to indicate below that we must
382 * get the partition number to set bm_inuse
383 * in the event we are trying to label this
384 * device. check_label is set when we are
385 * checking modifications for in use slices
386 * on the device.
387 */
388 found ++;
389 check = 1;
390 if (print) {
391 err_print(usage);
392 free(usage);
393 }
394 }
395 } else {
396 /*
397 * Before getting the in use data, verify that the
398 * current slice is within the range we are checking.
399 */
400 attrs = dm_get_attributes(slices[i], &error);
401 if (error) {
402 err_print("Error occurred with device in use "
403 "checking: %s\n", strerror(error));
404 continue;
405 }
406 if (attrs == NULL) {
407 continue;
408 }
409
410 (void) nvlist_lookup_uint64(attrs, DM_START,
411 &slice_start);
412 (void) nvlist_lookup_uint64(attrs, DM_SIZE,
413 &slice_size);
414 if (start >= (slice_start + slice_size) ||
415 (end < slice_start)) {
416 nvlist_free(attrs);
417 attrs = NULL;
418 continue;
419 }
420 name = dm_get_name(slices[i], &error);
421 if (error != 0 || !name) {
422 err_print("Error occurred with device "
423 "in use checking: %s\n", strerror(error));
424 nvlist_free(attrs);
425 attrs = NULL;
426 continue;
427 }
428 if (dm_inuse(name, &usage,
429 DM_WHO_FORMAT, &error) || error) {
430 if (error != 0) {
431 dm_free_name(name);
432 name = NULL;
433 err_print("Error occurred with "
434 "device in use checking: "
435 "%s\n", strerror(error));
436 nvlist_free(attrs);
437 attrs = NULL;
438 continue;
439 }
440 dm_free_name(name);
441 name = NULL;
442 /*
443 * If this is a dump device, then it is
444 * a failure. You cannot format a slice
445 * that is a dedicated dump device.
446 */
447 if (strstr(usage, DM_USE_DUMP)) {
448 if (print) {
449 err_print(usage);
450 free(usage);
451 }
452 dm_free_descriptors(slices);
453 nvlist_free(attrs);
454 return (1);
455 }
456 /*
457 * We really found a device that is in use.
458 * Set 'found' for the return value, and set
459 * 'check' to indicate below that we must
460 * get the partition number to set bm_inuse
461 * in the event we are trying to label this
462 * device. check_label is set when we are
463 * checking modifications for in use slices
464 * on the device.
465 */
466 found ++;
467 check = 1;
468 if (print) {
469 err_print(usage);
470 free(usage);
471 }
472 }
473 }
474 /*
475 * If check is set it means we found a slice(the current slice)
476 * on this device in use in some way. We potentially want
477 * to check this slice when labeling is
478 * requested. We set bm_inuse with this partition value
479 * for use later if check_label was set when called.
480 */
481 if (check) {
482 name = dm_get_name(slices[i], &error);
483 if (error != 0 || !name) {
484 err_print("Error occurred with device "
485 "in use checking: %s\n", strerror(error));
486 nvlist_free(attrs);
487 attrs = NULL;
488 continue;
489 }
490 part = getpartition(name);
491 dm_free_name(name);
492 name = NULL;
493 if (part != -1) {
494 bm_inuse |= 1 << part;
495 }
496 check = 0;
497 }
498 /*
499 * If we have attributes then we have successfully
500 * found the slice we were looking for and we also
501 * know this means we are not searching the whole
502 * disk so break out of the loop
503 * now.
504 */
505 if (attrs) {
506 nvlist_free(attrs);
507 break;
508 }
509 }
510
511 if (slices) {
512 dm_free_descriptors(slices);
513 }
514
515 /*
516 * The user is trying to label the disk. We have to do special
517 * checking here to ensure they are not trying to modify a slice
518 * that is in use in an incompatible way.
519 */
520 if (check_label && bm_inuse) {
521 /*
522 * !0 indicates that we found a
523 * problem. In this case, we have overloaded
524 * the use of checkpartitions to work for
525 * in use devices. bm_inuse is representative
526 * of the slice that is in use, not that
527 * is mounted as is in the case of the normal
528 * use of checkpartitions.
529 *
530 * The call to checkpartitions will return !0 if
531 * we are trying to shrink a device that we have found
532 * to be in use above.
533 */
534 return (checkpartitions(bm_inuse));
535 }
536
537 return (found);
538 }
539 /*
540 * This routine checks to see if there are mounted partitions overlapping
541 * a given portion of a disk. If the start parameter is < 0, it means
542 * that the entire disk should be checked.
543 */
544 int
checkmount(diskaddr_t start,diskaddr_t end)545 checkmount(diskaddr_t start, diskaddr_t end)
546 {
547 FILE *fp;
548 int found = 0;
549 struct dk_map32 *map;
550 int part;
551 struct mnttab mnt_record;
552 struct mnttab *mp = &mnt_record;
553
554 /*
555 * If we are only checking part of the disk, the disk must
556 * have a partition map to check against. If it doesn't,
557 * we hope for the best.
558 */
559 if (cur_parts == NULL)
560 return (0);
561
562 /*
563 * Lock out interrupts because of the mntent protocol.
564 */
565 enter_critical();
566 /*
567 * Open the mount table.
568 */
569 fp = fopen(MNTTAB, "r");
570 if (fp == NULL) {
571 err_print("Unable to open mount table.\n");
572 fullabort();
573 }
574 /*
575 * Loop through the mount table until we run out of entries.
576 */
577 while ((getmntent(fp, mp)) != -1) {
578
579 if ((part = getpartition(mp->mnt_special)) == -1)
580 continue;
581
582 /*
583 * It's a mount on the disk we're checking. If we are
584 * checking whole disk, then we found trouble. We can
585 * quit searching.
586 */
587 if (start == UINT_MAX64) {
588 found = -1;
589 break;
590 }
591
592 /*
593 * If the partition overlaps the zone we're checking,
594 * then we found trouble. We can quit searching.
595 */
596 map = &cur_parts->pinfo_map[part];
597 if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) ||
598 (end < (int)(map->dkl_cylno * spc()))) {
599 continue;
600 }
601 found = -1;
602 break;
603 }
604 /*
605 * Close down the mount table.
606 */
607 (void) fclose(fp);
608 exit_critical();
609
610 /*
611 * If we found trouble and we're running from a command file,
612 * quit before doing something we really regret.
613 */
614
615 if (found && option_f) {
616 err_print("Operation on mounted disks must be interactive.\n");
617 cmdabort(SIGINT);
618 }
619 /*
620 * Return the result.
621 */
622 return (found);
623 }
624
625 int
check_label_with_swap(void)626 check_label_with_swap(void)
627 {
628 int i;
629 struct swaptable *st;
630 struct swapent *swapent;
631 int part;
632 int bm_swap = 0;
633
634 /*
635 * If we are only checking part of the disk, the disk must
636 * have a partition map to check against. If it doesn't,
637 * we hope for the best.
638 */
639 if (cur_parts == NULL)
640 return (0); /* Will be checked later */
641
642 /*
643 * Check for swap entries
644 */
645 st = getswapentries();
646 /*
647 * if there are no swap entries return.
648 */
649 if (st == NULL)
650 return (0);
651 swapent = st->swt_ent;
652 for (i = 0; i < st->swt_n; i++, swapent++)
653 if ((part = getpartition(swapent->ste_path)) != -1)
654 bm_swap |= (1 << part);
655 freeswapentries(st);
656
657 return (checkpartitions(bm_swap));
658 }
659
660 /*
661 * Check the new label with the existing label on the disk,
662 * to make sure that any mounted partitions are not being
663 * affected by writing the new label.
664 */
665 int
check_label_with_mount(void)666 check_label_with_mount(void)
667 {
668 FILE *fp;
669 int part;
670 struct mnttab mnt_record;
671 struct mnttab *mp = &mnt_record;
672 int bm_mounted = 0;
673
674
675 /*
676 * If we are only checking part of the disk, the disk must
677 * have a partition map to check against. If it doesn't,
678 * we hope for the best.
679 */
680 if (cur_parts == NULL)
681 return (0); /* Will be checked later */
682
683 /*
684 * Lock out interrupts because of the mntent protocol.
685 */
686 enter_critical();
687 /*
688 * Open the mount table.
689 */
690 fp = fopen(MNTTAB, "r");
691 if (fp == NULL) {
692 err_print("Unable to open mount table.\n");
693 fullabort();
694 }
695 /*
696 * Loop through the mount table until we run out of entries.
697 */
698 while ((getmntent(fp, mp)) != -1) {
699 if ((part = getpartition(mp->mnt_special)) != -1)
700 bm_mounted |= (1 << part);
701 }
702 /*
703 * Close down the mount table.
704 */
705 (void) fclose(fp);
706 exit_critical();
707
708 return (checkpartitions(bm_mounted));
709
710 }
711
712 /*
713 * This Routine checks if any partitions specified
714 * are affected by writing the new label
715 */
716 static int
checkpartitions(int bm_mounted)717 checkpartitions(int bm_mounted)
718 {
719 struct dk_map32 *n;
720 struct dk_map *o;
721 struct dk_allmap old_map;
722 int i, found = 0;
723 struct partition64 o_efi;
724
725 /*
726 * Now we need to check that the current partition list and the
727 * previous partition list (which there must be if we actually
728 * have partitions mounted) overlap in any way on the mounted
729 * partitions
730 */
731
732 /*
733 * Check if the user wants to online-label an
734 * existing EFI label.
735 */
736 if (cur_label == L_TYPE_EFI) {
737 for (i = 0; i < EFI_NUMPAR; i++) {
738 if (bm_mounted & (1 << i)) {
739 o_efi.p_partno = i;
740 if (ioctl(cur_file, DKIOCPARTITION, &o_efi)
741 == -1) {
742 err_print("Unable to get information "
743 "for EFI partition %d.\n", i);
744 return (-1);
745 }
746
747 /*
748 * Partition can grow or remain same.
749 */
750 if (o_efi.p_start == cur_parts->etoc->
751 efi_parts[i].p_start && o_efi.p_size
752 <= cur_parts->etoc->efi_parts[i].p_size) {
753 continue;
754 }
755
756 found = -1;
757 }
758 if (found)
759 break;
760 }
761
762 } else {
763
764 /*
765 * Get the "real" (on-disk) version of the partition table
766 */
767 if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) {
768 err_print("Unable to get current partition map.\n");
769 return (-1);
770 }
771 for (i = 0; i < NDKMAP; i++) {
772 if (bm_mounted & (1 << i)) {
773 /*
774 * This partition is mounted
775 */
776 o = &old_map.dka_map[i];
777 n = &cur_parts->pinfo_map[i];
778 #ifdef DEBUG
779 fmt_print(
780 "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE);
781 #endif
782 /*
783 * If partition is identical, we're fine.
784 * If the partition grows, we're also fine,
785 * because the routines in partition.c check
786 * for overflow. It will (ultimately) be up
787 * to the routines in partition.c to warn
788 * about creation of overlapping partitions.
789 */
790 if (o->dkl_cylno == n->dkl_cylno &&
791 o->dkl_nblk <= n->dkl_nblk) {
792 #ifdef DEBUG
793 if (o->dkl_nblk < n->dkl_nblk) {
794 fmt_print(
795 "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk);
796 }
797 fmt_print("\n");
798 #endif
799 continue;
800 }
801 #ifdef DEBUG
802 fmt_print("- changes; old (%d,%d)->new "
803 "(%d,%d)\n", o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, n->dkl_nblk);
804 #endif
805 found = -1;
806 }
807 if (found)
808 break;
809 }
810 }
811
812 /*
813 * If we found trouble and we're running from a command file,
814 * quit before doing something we really regret.
815 */
816
817 if (found && option_f) {
818 err_print("Operation on mounted disks or \
819 disks currently being used for swapping must be interactive.\n");
820 cmdabort(SIGINT);
821 }
822 /*
823 * Return the result.
824 */
825 return (found);
826 }
827