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