xref: /illumos-gate/usr/src/cmd/format/checkdev.c (revision cb1bb6c32d034ea24e8549ef763c9c2b79413eb8)
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 *
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
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
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
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
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
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
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
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
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