xref: /illumos-gate/usr/src/cmd/format/checkdev.c (revision 141040e8a310da49386b596573e5dde5580572ec)
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 	 * If the user does not want to do in use checking, return immediately.
288 	 * Normally, this is handled in libdiskmgt. For format, there is more
289 	 * processing required, so we want to bypass the in use checking
290 	 * here.
291 	 */
292 
293 	if (NOINUSE_SET)
294 		return (0);
295 
296 	/*
297 	 * For format, we get basic 'in use' details from libdiskmgt. After
298 	 * that we must do the appropriate checking to see if the 'in use'
299 	 * details require a bit of additional work.
300 	 */
301 
302 	dm_get_slices(cur_disk_path, &slices, &error);
303 	if (error) {
304 		err_print("Error occurred with device in use checking: %s\n",
305 		    strerror(error));
306 		return (found);
307 	}
308 	if (slices == NULL)
309 		return (found);
310 
311 	for (i = 0; slices[i] != NULL; i++) {
312 		/*
313 		 * If we are checking the whole disk
314 		 * then any and all in use data is
315 		 * relevant.
316 		 */
317 		if (start == UINT_MAX64) {
318 			name = dm_get_name(slices[i], &error);
319 			if (error != 0 || !name) {
320 				err_print("Error occurred with device "
321 				    "in use checking: %s\n",
322 				    strerror(error));
323 				continue;
324 			}
325 			if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) ||
326 			    error) {
327 				if (error != 0) {
328 					dm_free_name(name);
329 					name = NULL;
330 					err_print("Error occurred with device "
331 					    "in use checking: %s\n",
332 					    strerror(error));
333 					continue;
334 				}
335 				dm_free_name(name);
336 				name = NULL;
337 				/*
338 				 * If this is a dump device, then it is
339 				 * a failure. You cannot format a slice
340 				 * that is a dedicated dump device.
341 				 */
342 
343 				if (strstr(usage, DM_USE_DUMP)) {
344 					if (print) {
345 						err_print(usage);
346 						free(usage);
347 					}
348 					dm_free_descriptors(slices);
349 					return (1);
350 				}
351 				/*
352 				 * We really found a device that is in use.
353 				 * Set 'found' for the return value, and set
354 				 * 'check' to indicate below that we must
355 				 * get the partition number to set bm_inuse
356 				 * in the event we are trying to label this
357 				 * device. check_label is set when we are
358 				 * checking modifications for in use slices
359 				 * on the device.
360 				 */
361 				found ++;
362 				check = 1;
363 				if (print) {
364 					err_print(usage);
365 					free(usage);
366 				}
367 			}
368 		} else {
369 			/*
370 			 * Before getting the in use data, verify that the
371 			 * current slice is within the range we are checking.
372 			 */
373 			attrs = dm_get_attributes(slices[i], &error);
374 			if (error) {
375 				err_print("Error occurred with device in use "
376 				    "checking: %s\n", strerror(error));
377 				continue;
378 			}
379 			if (attrs == NULL) {
380 				continue;
381 			}
382 
383 			(void) nvlist_lookup_uint64(attrs, DM_START,
384 			    &slice_start);
385 			(void) nvlist_lookup_uint64(attrs, DM_SIZE,
386 			    &slice_size);
387 			if (start >= (slice_start + slice_size) ||
388 			    (end < slice_start)) {
389 				nvlist_free(attrs);
390 				attrs = NULL;
391 				continue;
392 			}
393 			name = dm_get_name(slices[i], &error);
394 			if (error != 0 || !name) {
395 				err_print("Error occurred with device "
396 				    "in use checking: %s\n",
397 				    strerror(error));
398 				nvlist_free(attrs);
399 				attrs = NULL;
400 				continue;
401 			}
402 			if (dm_inuse(name, &usage,
403 			    DM_WHO_FORMAT, &error) || error) {
404 				if (error != 0) {
405 					dm_free_name(name);
406 					name = NULL;
407 					err_print("Error occurred with device "
408 					    "in use checking: %s\n",
409 					    strerror(error));
410 					nvlist_free(attrs);
411 					attrs = NULL;
412 					continue;
413 				}
414 				dm_free_name(name);
415 				name = NULL;
416 				/*
417 				 * If this is a dump device, then it is
418 				 * a failure. You cannot format a slice
419 				 * that is a dedicated dump device.
420 				 */
421 				if (strstr(usage, DM_USE_DUMP)) {
422 					if (print) {
423 						err_print(usage);
424 						free(usage);
425 					}
426 					dm_free_descriptors(slices);
427 					nvlist_free(attrs);
428 					return (1);
429 				}
430 				/*
431 				 * We really found a device that is in use.
432 				 * Set 'found' for the return value, and set
433 				 * 'check' to indicate below that we must
434 				 * get the partition number to set bm_inuse
435 				 * in the event we are trying to label this
436 				 * device. check_label is set when we are
437 				 * checking modifications for in use slices
438 				 * on the device.
439 				 */
440 				found ++;
441 				check = 1;
442 				if (print) {
443 					err_print(usage);
444 					free(usage);
445 				}
446 			}
447 		}
448 		/*
449 		 * If check is set it means we found a slice(the current slice)
450 		 * on this device in use in some way.  We potentially want
451 		 * to check this slice when labeling is
452 		 * requested. We set bm_inuse with this partition value
453 		 * for use later if check_label was set when called.
454 		 */
455 		if (check) {
456 			name = dm_get_name(slices[i], &error);
457 			if (error != 0 || !name) {
458 				err_print("Error occurred with device "
459 				    "in use checking: %s\n",
460 				    strerror(error));
461 				nvlist_free(attrs);
462 				attrs = NULL;
463 				continue;
464 			}
465 			part = getpartition(name);
466 			dm_free_name(name);
467 			name = NULL;
468 			if (part != -1) {
469 				bm_inuse |= 1 << part;
470 			}
471 			check = 0;
472 		}
473 		/*
474 		 * If we have attributes then we have successfully
475 		 * found the slice we were looking for and we also
476 		 * know this means we are not searching the whole
477 		 * disk so break out of the loop
478 		 * now.
479 		 */
480 		if (attrs) {
481 			nvlist_free(attrs);
482 			break;
483 		}
484 	}
485 
486 	if (slices) {
487 		dm_free_descriptors(slices);
488 	}
489 
490 	/*
491 	 * The user is trying to label the disk. We have to do special
492 	 * checking here to ensure they are not trying to modify a slice
493 	 * that is in use in an incompatible way.
494 	 */
495 	if (check_label && bm_inuse) {
496 		/*
497 		 * !0 indicates that we found a
498 		 * problem. In this case, we have overloaded
499 		 * the use of checkpartitions to work for
500 		 * in use devices. bm_inuse is representative
501 		 * of the slice that is in use, not that
502 		 * is mounted as is in the case of the normal
503 		 * use of checkpartitions.
504 		 *
505 		 * The call to checkpartitions will return !0 if
506 		 * we are trying to shrink a device that we have found
507 		 * to be in use above.
508 		 */
509 		return (checkpartitions(bm_inuse));
510 	}
511 
512 	return (found);
513 }
514 /*
515  * This routine checks to see if there are mounted partitions overlapping
516  * a given portion of a disk.  If the start parameter is < 0, it means
517  * that the entire disk should be checked.
518  */
519 int
520 checkmount(start, end)
521 	diskaddr_t	start, end;
522 {
523 	FILE		*fp;
524 	int		found = 0;
525 	struct dk_map32	*map;
526 	int		part;
527 	struct mnttab	mnt_record;
528 	struct mnttab	*mp = &mnt_record;
529 
530 	/*
531 	 * If we are only checking part of the disk, the disk must
532 	 * have a partition map to check against.  If it doesn't,
533 	 * we hope for the best.
534 	 */
535 	if (cur_parts == NULL)
536 		return (0);
537 
538 	/*
539 	 * Lock out interrupts because of the mntent protocol.
540 	 */
541 	enter_critical();
542 	/*
543 	 * Open the mount table.
544 	 */
545 	fp = fopen(MNTTAB, "r");
546 	if (fp == NULL) {
547 		err_print("Unable to open mount table.\n");
548 		fullabort();
549 	}
550 	/*
551 	 * Loop through the mount table until we run out of entries.
552 	 */
553 	while ((getmntent(fp, mp)) != -1) {
554 
555 		if ((part = getpartition(mp->mnt_special)) == -1)
556 			continue;
557 
558 		/*
559 		 * It's a mount on the disk we're checking.  If we are
560 		 * checking whole disk, then we found trouble.  We can
561 		 * quit searching.
562 		 */
563 		if (start == UINT_MAX64) {
564 			found = -1;
565 			break;
566 		}
567 
568 		/*
569 		 * If the partition overlaps the zone we're checking,
570 		 * then we found trouble.  We can quit searching.
571 		 */
572 		map = &cur_parts->pinfo_map[part];
573 		if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) ||
574 			(end < (int)(map->dkl_cylno * spc()))) {
575 			continue;
576 		}
577 		found = -1;
578 		break;
579 	}
580 	/*
581 	 * Close down the mount table.
582 	 */
583 	(void) fclose(fp);
584 	exit_critical();
585 
586 	/*
587 	 * If we found trouble and we're running from a command file,
588 	 * quit before doing something we really regret.
589 	 */
590 
591 	if (found && option_f) {
592 		err_print("Operation on mounted disks must be interactive.\n");
593 		cmdabort(SIGINT);
594 	}
595 	/*
596 	 * Return the result.
597 	 */
598 	return (found);
599 }
600 
601 int
602 check_label_with_swap()
603 {
604 	int			i;
605 	struct swaptable *st;
606 	struct swapent *swapent;
607 	int	part;
608 	int	bm_swap = 0;
609 
610 	/*
611 	 * If we are only checking part of the disk, the disk must
612 	 * have a partition map to check against.  If it doesn't,
613 	 * we hope for the best.
614 	 */
615 	if (cur_parts == NULL)
616 		return (0);	/* Will be checked later */
617 
618 	/*
619 	 * Check for swap entries
620 	 */
621 	st = getswapentries();
622 	/*
623 	 * if there are no swap entries return.
624 	 */
625 	if (st == (struct swaptable *)NULL)
626 		return (0);
627 	swapent = st->swt_ent;
628 	for (i = 0; i < st->swt_n; i++, swapent++)
629 		if ((part = getpartition(swapent->ste_path)) != -1)
630 				bm_swap |= (1 << part);
631 	freeswapentries(st);
632 
633 	return (checkpartitions(bm_swap));
634 }
635 
636 /*
637  * Check the new label with the existing label on the disk,
638  * to make sure that any mounted partitions are not being
639  * affected by writing the new label.
640  */
641 int
642 check_label_with_mount()
643 {
644 	FILE			*fp;
645 	int			part;
646 	struct mnttab		mnt_record;
647 	struct mnttab		*mp = &mnt_record;
648 	int			bm_mounted = 0;
649 
650 
651 	/*
652 	 * If we are only checking part of the disk, the disk must
653 	 * have a partition map to check against.  If it doesn't,
654 	 * we hope for the best.
655 	 */
656 	if (cur_parts == NULL)
657 		return (0);	/* Will be checked later */
658 
659 	/*
660 	 * Lock out interrupts because of the mntent protocol.
661 	 */
662 	enter_critical();
663 	/*
664 	 * Open the mount table.
665 	 */
666 	fp = fopen(MNTTAB, "r");
667 	if (fp == NULL) {
668 		err_print("Unable to open mount table.\n");
669 		fullabort();
670 	}
671 	/*
672 	 * Loop through the mount table until we run out of entries.
673 	 */
674 	while ((getmntent(fp, mp)) != -1) {
675 		if ((part = getpartition(mp->mnt_special)) != -1)
676 			bm_mounted |= (1 << part);
677 	}
678 	/*
679 	 * Close down the mount table.
680 	 */
681 	(void) fclose(fp);
682 	exit_critical();
683 
684 	return (checkpartitions(bm_mounted));
685 
686 }
687 
688 /*
689  * This Routine checks if any partitions specified
690  * are affected by writing the new label
691  */
692 static int
693 checkpartitions(int bm_mounted)
694 {
695 	struct dk_map32		*n;
696 	struct dk_map		*o;
697 	struct dk_allmap	old_map;
698 	int			i, found = 0;
699 
700 	/*
701 	 * Now we need to check that the current partition list and the
702 	 * previous partition list (which there must be if we actually
703 	 * have partitions mounted) overlap  in any way on the mounted
704 	 * partitions
705 	 */
706 
707 	/*
708 	 * Get the "real" (on-disk) version of the partition table
709 	 */
710 	if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) {
711 		err_print("Unable to get current partition map.\n");
712 		return (-1);
713 	}
714 	for (i = 0; i < NDKMAP; i++) {
715 		if (bm_mounted & (1 << i)) {
716 			/*
717 			 * This partition is mounted
718 			 */
719 			o = &old_map.dka_map[i];
720 			n = &cur_parts->pinfo_map[i];
721 #ifdef DEBUG
722 			fmt_print(
723 "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE);
724 #endif
725 			/*
726 			 * If partition is identical, we're fine.
727 			 * If the partition grows, we're also fine, because
728 			 * the routines in partition.c check for overflow.
729 			 * It will (ultimately) be up to the routines in
730 			 * partition.c to warn about creation of overlapping
731 			 * partitions
732 			 */
733 			if (o->dkl_cylno == n->dkl_cylno &&
734 					o->dkl_nblk <= n->dkl_nblk) {
735 #ifdef	DEBUG
736 				if (o->dkl_nblk < n->dkl_nblk) {
737 					fmt_print(
738 "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk);
739 				}
740 				fmt_print("\n");
741 #endif
742 				continue;
743 			}
744 #ifdef DEBUG
745 			fmt_print("- changes; old (%d,%d)->new (%d,%d)\n",
746 				o->dkl_cylno, o->dkl_nblk, n->dkl_cylno,
747 				n->dkl_nblk);
748 #endif
749 			found = -1;
750 		}
751 		if (found)
752 			break;
753 	}
754 
755 	/*
756 	 * If we found trouble and we're running from a command file,
757 	 * quit before doing something we really regret.
758 	 */
759 
760 	if (found && option_f) {
761 		err_print("Operation on mounted disks or \
762 disks currently being used for swapping must be interactive.\n");
763 		cmdabort(SIGINT);
764 	}
765 	/*
766 	 * Return the result.
767 	 */
768 	return (found);
769 }
770