comparison usr/src/cmd/format/checkdev.c @ 0:c9caec207d52 b86

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