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