13e1bd7a2Ssjelinek /* 23e1bd7a2Ssjelinek * CDDL HEADER START 33e1bd7a2Ssjelinek * 43e1bd7a2Ssjelinek * The contents of this file are subject to the terms of the 5d25b227dSzl149053 * Common Development and Distribution License (the "License"). 6d25b227dSzl149053 * You may not use this file except in compliance with the License. 73e1bd7a2Ssjelinek * 83e1bd7a2Ssjelinek * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93e1bd7a2Ssjelinek * or http://www.opensolaris.org/os/licensing. 103e1bd7a2Ssjelinek * See the License for the specific language governing permissions 113e1bd7a2Ssjelinek * and limitations under the License. 123e1bd7a2Ssjelinek * 133e1bd7a2Ssjelinek * When distributing Covered Code, include this CDDL HEADER in each 143e1bd7a2Ssjelinek * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153e1bd7a2Ssjelinek * If applicable, add the following below this CDDL HEADER, with the 163e1bd7a2Ssjelinek * fields enclosed by brackets "[]" replaced with your own identifying 173e1bd7a2Ssjelinek * information: Portions Copyright [yyyy] [name of copyright owner] 183e1bd7a2Ssjelinek * 193e1bd7a2Ssjelinek * CDDL HEADER END 203e1bd7a2Ssjelinek */ 213e1bd7a2Ssjelinek /* 22*2b237d4bSny155746 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 233e1bd7a2Ssjelinek * Use is subject to license terms. 243e1bd7a2Ssjelinek */ 253e1bd7a2Ssjelinek 263e1bd7a2Ssjelinek 273e1bd7a2Ssjelinek #pragma ident "%Z%%M% %I% %E% SMI" 283e1bd7a2Ssjelinek 293e1bd7a2Ssjelinek /* 303e1bd7a2Ssjelinek * This file contains miscellaneous device validation routines. 313e1bd7a2Ssjelinek */ 323e1bd7a2Ssjelinek 333e1bd7a2Ssjelinek #include "global.h" 343e1bd7a2Ssjelinek #include <sys/mnttab.h> 353e1bd7a2Ssjelinek #include <sys/mntent.h> 363e1bd7a2Ssjelinek #include <sys/autoconf.h> 373e1bd7a2Ssjelinek 383e1bd7a2Ssjelinek #include <signal.h> 393e1bd7a2Ssjelinek #include <malloc.h> 403e1bd7a2Ssjelinek #include <unistd.h> 413e1bd7a2Ssjelinek #include <string.h> 423e1bd7a2Ssjelinek #include <errno.h> 433e1bd7a2Ssjelinek #include <fcntl.h> 44d25b227dSzl149053 #include <libgen.h> 453e1bd7a2Ssjelinek #include <sys/ioctl.h> 463e1bd7a2Ssjelinek #include <sys/fcntl.h> 473e1bd7a2Ssjelinek #include <sys/stat.h> 483e1bd7a2Ssjelinek #include <sys/swap.h> 493e1bd7a2Ssjelinek #include <sys/sysmacros.h> 50d25b227dSzl149053 #include <sys/mkdev.h> 51d25b227dSzl149053 #include <sys/modctl.h> 523e1bd7a2Ssjelinek #include <ctype.h> 533e1bd7a2Ssjelinek #include <libdiskmgt.h> 543e1bd7a2Ssjelinek #include <libnvpair.h> 553e1bd7a2Ssjelinek #include "misc.h" 563e1bd7a2Ssjelinek #include "checkdev.h" 573e1bd7a2Ssjelinek 583e1bd7a2Ssjelinek /* Function prototypes */ 593e1bd7a2Ssjelinek #ifdef __STDC__ 603e1bd7a2Ssjelinek 613e1bd7a2Ssjelinek static struct swaptable *getswapentries(void); 623e1bd7a2Ssjelinek static void freeswapentries(struct swaptable *); 633e1bd7a2Ssjelinek static int getpartition(char *pathname); 643e1bd7a2Ssjelinek static int checkpartitions(int bm_mounted); 653e1bd7a2Ssjelinek 663e1bd7a2Ssjelinek #else /* __STDC__ */ 673e1bd7a2Ssjelinek 683e1bd7a2Ssjelinek static struct swaptable *getswapentries(); 693e1bd7a2Ssjelinek static void freeswapentries(); 703e1bd7a2Ssjelinek static int getpartition(); 713e1bd7a2Ssjelinek static int checkpartitions(); 723e1bd7a2Ssjelinek 733e1bd7a2Ssjelinek #endif /* __STDC__ */ 743e1bd7a2Ssjelinek 753e1bd7a2Ssjelinek extern char *getfullname(); 763e1bd7a2Ssjelinek 773e1bd7a2Ssjelinek static struct swaptable * 783e1bd7a2Ssjelinek getswapentries(void) 793e1bd7a2Ssjelinek { 803e1bd7a2Ssjelinek register struct swaptable *st; 813e1bd7a2Ssjelinek register struct swapent *swapent; 823e1bd7a2Ssjelinek int i, num; 833e1bd7a2Ssjelinek char fullpathname[MAXPATHLEN]; 843e1bd7a2Ssjelinek 853e1bd7a2Ssjelinek /* 863e1bd7a2Ssjelinek * get the number of swap entries 873e1bd7a2Ssjelinek */ 883e1bd7a2Ssjelinek if ((num = swapctl(SC_GETNSWP, (void *)NULL)) == -1) { 893e1bd7a2Ssjelinek err_print("swapctl error "); 903e1bd7a2Ssjelinek fullabort(); 913e1bd7a2Ssjelinek } 923e1bd7a2Ssjelinek if (num == 0) 933e1bd7a2Ssjelinek return (NULL); 943e1bd7a2Ssjelinek if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int))) 953e1bd7a2Ssjelinek == NULL) { 963e1bd7a2Ssjelinek err_print("getswapentries: malloc failed.\n"); 973e1bd7a2Ssjelinek fullabort(); 983e1bd7a2Ssjelinek } 993e1bd7a2Ssjelinek swapent = st->swt_ent; 1003e1bd7a2Ssjelinek for (i = 0; i < num; i++, swapent++) { 1013e1bd7a2Ssjelinek if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) { 1023e1bd7a2Ssjelinek err_print("getswapentries: malloc failed.\n"); 1033e1bd7a2Ssjelinek fullabort(); 1043e1bd7a2Ssjelinek } 1053e1bd7a2Ssjelinek } 1063e1bd7a2Ssjelinek st->swt_n = num; 1073e1bd7a2Ssjelinek if ((num = swapctl(SC_LIST, (void *)st)) == -1) { 1083e1bd7a2Ssjelinek err_print("swapctl error "); 1093e1bd7a2Ssjelinek fullabort(); 1103e1bd7a2Ssjelinek } 1113e1bd7a2Ssjelinek swapent = st->swt_ent; 1123e1bd7a2Ssjelinek for (i = 0; i < num; i++, swapent++) { 1133e1bd7a2Ssjelinek if (*swapent->ste_path != '/') { 1143e1bd7a2Ssjelinek (void) snprintf(fullpathname, sizeof (fullpathname), 1153e1bd7a2Ssjelinek "/dev/%s", swapent->ste_path); 1163e1bd7a2Ssjelinek (void) strcpy(swapent->ste_path, fullpathname); 1173e1bd7a2Ssjelinek } 1183e1bd7a2Ssjelinek } 1193e1bd7a2Ssjelinek return (st); 1203e1bd7a2Ssjelinek } 1213e1bd7a2Ssjelinek 1223e1bd7a2Ssjelinek static void 1233e1bd7a2Ssjelinek freeswapentries(st) 1243e1bd7a2Ssjelinek struct swaptable *st; 1253e1bd7a2Ssjelinek { 1263e1bd7a2Ssjelinek register struct swapent *swapent; 1273e1bd7a2Ssjelinek int i; 1283e1bd7a2Ssjelinek 1293e1bd7a2Ssjelinek swapent = st->swt_ent; 1303e1bd7a2Ssjelinek for (i = 0; i < st->swt_n; i++, swapent++) 1313e1bd7a2Ssjelinek free(swapent->ste_path); 1323e1bd7a2Ssjelinek free(st); 1333e1bd7a2Ssjelinek 1343e1bd7a2Ssjelinek } 1353e1bd7a2Ssjelinek 1363e1bd7a2Ssjelinek /* 1373e1bd7a2Ssjelinek * function getpartition: 1383e1bd7a2Ssjelinek */ 1393e1bd7a2Ssjelinek static int 1403e1bd7a2Ssjelinek getpartition(pathname) 1413e1bd7a2Ssjelinek char *pathname; 1423e1bd7a2Ssjelinek { 1433e1bd7a2Ssjelinek int mfd; 1443e1bd7a2Ssjelinek struct dk_cinfo dkinfo; 1453e1bd7a2Ssjelinek struct stat stbuf; 1463e1bd7a2Ssjelinek char raw_device[MAXPATHLEN]; 1473e1bd7a2Ssjelinek int found = -1; 1483e1bd7a2Ssjelinek 1493e1bd7a2Ssjelinek /* 1503e1bd7a2Ssjelinek * Map the block device name to the raw device name. 1513e1bd7a2Ssjelinek * If it doesn't appear to be a device name, skip it. 1523e1bd7a2Ssjelinek */ 1533e1bd7a2Ssjelinek if (match_substr(pathname, "/dev/") == 0) 1543e1bd7a2Ssjelinek return (found); 1553e1bd7a2Ssjelinek (void) strcpy(raw_device, "/dev/r"); 1563e1bd7a2Ssjelinek (void) strcat(raw_device, pathname + strlen("/dev/")); 1573e1bd7a2Ssjelinek /* 1583e1bd7a2Ssjelinek * Determine if this appears to be a disk device. 1593e1bd7a2Ssjelinek * First attempt to open the device. If if fails, skip it. 1603e1bd7a2Ssjelinek */ 1613e1bd7a2Ssjelinek if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) { 1623e1bd7a2Ssjelinek return (found); 1633e1bd7a2Ssjelinek } 1643e1bd7a2Ssjelinek /* 1653e1bd7a2Ssjelinek * Must be a character device 1663e1bd7a2Ssjelinek */ 1673e1bd7a2Ssjelinek if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) { 1683e1bd7a2Ssjelinek (void) close(mfd); 1693e1bd7a2Ssjelinek return (found); 1703e1bd7a2Ssjelinek } 1713e1bd7a2Ssjelinek /* 1723e1bd7a2Ssjelinek * Attempt to read the configuration info on the disk. 1733e1bd7a2Ssjelinek */ 1743e1bd7a2Ssjelinek if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) { 1753e1bd7a2Ssjelinek (void) close(mfd); 1763e1bd7a2Ssjelinek return (found); 1773e1bd7a2Ssjelinek } 1783e1bd7a2Ssjelinek /* 1793e1bd7a2Ssjelinek * Finished with the opened device 1803e1bd7a2Ssjelinek */ 1813e1bd7a2Ssjelinek (void) close(mfd); 1823e1bd7a2Ssjelinek 1833e1bd7a2Ssjelinek /* 1843e1bd7a2Ssjelinek * If it's not the disk we're interested in, it doesn't apply. 1853e1bd7a2Ssjelinek */ 1863e1bd7a2Ssjelinek if (cur_disk->disk_dkinfo.dki_ctype != dkinfo.dki_ctype || 1873e1bd7a2Ssjelinek cur_disk->disk_dkinfo.dki_cnum != dkinfo.dki_cnum || 1883e1bd7a2Ssjelinek cur_disk->disk_dkinfo.dki_unit != dkinfo.dki_unit || 1893e1bd7a2Ssjelinek strcmp(cur_disk->disk_dkinfo.dki_dname, 1903e1bd7a2Ssjelinek dkinfo.dki_dname) != 0) { 1913e1bd7a2Ssjelinek return (found); 1923e1bd7a2Ssjelinek } 1933e1bd7a2Ssjelinek 1943e1bd7a2Ssjelinek /* 1953e1bd7a2Ssjelinek * Extract the partition that is mounted. 1963e1bd7a2Ssjelinek */ 1973e1bd7a2Ssjelinek return (PARTITION(stbuf.st_rdev)); 1983e1bd7a2Ssjelinek } 1993e1bd7a2Ssjelinek 2003e1bd7a2Ssjelinek /* 2013e1bd7a2Ssjelinek * This Routine checks to see if there are partitions used for swapping overlaps 2023e1bd7a2Ssjelinek * a given portion of a disk. If the start parameter is < 0, it means 2033e1bd7a2Ssjelinek * that the entire disk should be checked 2043e1bd7a2Ssjelinek */ 2053e1bd7a2Ssjelinek int 2063e1bd7a2Ssjelinek checkswap(start, end) 2073e1bd7a2Ssjelinek diskaddr_t start, end; 2083e1bd7a2Ssjelinek { 2093e1bd7a2Ssjelinek struct swaptable *st; 2103e1bd7a2Ssjelinek struct swapent *swapent; 2113e1bd7a2Ssjelinek int i; 2123e1bd7a2Ssjelinek int found = 0; 2133e1bd7a2Ssjelinek struct dk_map32 *map; 2143e1bd7a2Ssjelinek int part; 2153e1bd7a2Ssjelinek 2163e1bd7a2Ssjelinek /* 2173e1bd7a2Ssjelinek * If we are only checking part of the disk, the disk must 2183e1bd7a2Ssjelinek * have a partition map to check against. If it doesn't, 2193e1bd7a2Ssjelinek * we hope for the best. 2203e1bd7a2Ssjelinek */ 2213e1bd7a2Ssjelinek if (cur_parts == NULL) 2223e1bd7a2Ssjelinek return (0); 2233e1bd7a2Ssjelinek 2243e1bd7a2Ssjelinek /* 2253e1bd7a2Ssjelinek * check for swap entries 2263e1bd7a2Ssjelinek */ 2273e1bd7a2Ssjelinek st = getswapentries(); 2283e1bd7a2Ssjelinek /* 2293e1bd7a2Ssjelinek * if there are no swap entries return. 2303e1bd7a2Ssjelinek */ 2313e1bd7a2Ssjelinek if (st == (struct swaptable *)NULL) 2323e1bd7a2Ssjelinek return (0); 2333e1bd7a2Ssjelinek swapent = st->swt_ent; 2343e1bd7a2Ssjelinek for (i = 0; i < st->swt_n; i++, swapent++) { 2353e1bd7a2Ssjelinek if ((part = getpartition(swapent->ste_path)) != -1) { 2363e1bd7a2Ssjelinek if (start == UINT_MAX64) { 2373e1bd7a2Ssjelinek found = -1; 2383e1bd7a2Ssjelinek break; 2393e1bd7a2Ssjelinek } 2403e1bd7a2Ssjelinek map = &cur_parts->pinfo_map[part]; 2413e1bd7a2Ssjelinek if ((start >= (int)(map->dkl_cylno * spc() + 2423e1bd7a2Ssjelinek map->dkl_nblk)) || (end < (int)(map->dkl_cylno 2433e1bd7a2Ssjelinek * spc()))) { 2443e1bd7a2Ssjelinek continue; 2453e1bd7a2Ssjelinek } 2463e1bd7a2Ssjelinek found = -1; 2473e1bd7a2Ssjelinek break; 2483e1bd7a2Ssjelinek }; 2493e1bd7a2Ssjelinek } 2503e1bd7a2Ssjelinek freeswapentries(st); 2513e1bd7a2Ssjelinek /* 2523e1bd7a2Ssjelinek * If we found trouble and we're running from a command file, 2533e1bd7a2Ssjelinek * quit before doing something we really regret. 2543e1bd7a2Ssjelinek */ 2553e1bd7a2Ssjelinek 2563e1bd7a2Ssjelinek if (found && option_f) { 2573e1bd7a2Ssjelinek err_print( 2583e1bd7a2Ssjelinek "Operation on disks being used for swapping must be interactive.\n"); 2593e1bd7a2Ssjelinek cmdabort(SIGINT); 2603e1bd7a2Ssjelinek } 2613e1bd7a2Ssjelinek 2623e1bd7a2Ssjelinek return (found); 2633e1bd7a2Ssjelinek 2643e1bd7a2Ssjelinek 2653e1bd7a2Ssjelinek } 2663e1bd7a2Ssjelinek /* 2673e1bd7a2Ssjelinek * Determines if there are partitions that are a part of an SVM, VxVM, zpool 2683e1bd7a2Ssjelinek * volume or a live upgrade device, overlapping a given portion of a disk. 2693e1bd7a2Ssjelinek * Mounts and swap devices are checked in legacy format code. 2703e1bd7a2Ssjelinek */ 2713e1bd7a2Ssjelinek int 2723e1bd7a2Ssjelinek checkdevinuse(char *cur_disk_path, diskaddr_t start, diskaddr_t end, int print, 2733e1bd7a2Ssjelinek int check_label) 2743e1bd7a2Ssjelinek { 2753e1bd7a2Ssjelinek 2763e1bd7a2Ssjelinek int error; 2773e1bd7a2Ssjelinek int found = 0; 2783e1bd7a2Ssjelinek int check = 0; 2793e1bd7a2Ssjelinek int i; 2803e1bd7a2Ssjelinek int bm_inuse = 0; 2813e1bd7a2Ssjelinek int part = 0; 2823e1bd7a2Ssjelinek uint64_t slice_start, slice_size; 2833e1bd7a2Ssjelinek dm_descriptor_t *slices = NULL; 2843e1bd7a2Ssjelinek nvlist_t *attrs = NULL; 2853e1bd7a2Ssjelinek char *usage; 2863e1bd7a2Ssjelinek char *name; 2873e1bd7a2Ssjelinek 2883e1bd7a2Ssjelinek /* 28982d71480Ssjelinek * If the user does not want to do in use checking, return immediately. 29082d71480Ssjelinek * Normally, this is handled in libdiskmgt. For format, there is more 29182d71480Ssjelinek * processing required, so we want to bypass the in use checking 29282d71480Ssjelinek * here. 29382d71480Ssjelinek */ 29482d71480Ssjelinek 29582d71480Ssjelinek if (NOINUSE_SET) 29682d71480Ssjelinek return (0); 29782d71480Ssjelinek 29882d71480Ssjelinek /* 299d25b227dSzl149053 * Skip if it is not a real disk 300d25b227dSzl149053 * 301d25b227dSzl149053 * There could be two kinds of strings in cur_disk_path 302d25b227dSzl149053 * One starts with c?t?d?, while the other is a absolute path of a 303d25b227dSzl149053 * block device file. 304d25b227dSzl149053 */ 305d25b227dSzl149053 306d25b227dSzl149053 if (*cur_disk_path != 'c') { 307d25b227dSzl149053 struct stat stbuf; 308d25b227dSzl149053 char majorname[16]; 309d25b227dSzl149053 major_t majornum; 310d25b227dSzl149053 311d25b227dSzl149053 (void) stat(cur_disk_path, &stbuf); 312d25b227dSzl149053 majornum = major(stbuf.st_rdev); 313d25b227dSzl149053 (void) modctl(MODGETNAME, majorname, sizeof (majorname), 314d25b227dSzl149053 &majornum); 315d25b227dSzl149053 316d25b227dSzl149053 if (strcmp(majorname, "sd")) 317d25b227dSzl149053 if (strcmp(majorname, "ssd")) 318d25b227dSzl149053 if (strcmp(majorname, "cmdk")) 319d25b227dSzl149053 return (0); 320d25b227dSzl149053 } 321d25b227dSzl149053 322d25b227dSzl149053 /* 323d25b227dSzl149053 * Truncate the characters following "d*", such as "s*" or "p*" 324d25b227dSzl149053 */ 325d25b227dSzl149053 cur_disk_path = basename(cur_disk_path); 326d25b227dSzl149053 name = strrchr(cur_disk_path, 'd'); 327d25b227dSzl149053 if (name) { 328d25b227dSzl149053 name++; 329d25b227dSzl149053 for (; (*name <= '9') && (*name >= '0'); name++); 330d25b227dSzl149053 *name = (char)0; 331d25b227dSzl149053 } 332d25b227dSzl149053 333d25b227dSzl149053 334d25b227dSzl149053 /* 3353e1bd7a2Ssjelinek * For format, we get basic 'in use' details from libdiskmgt. After 3363e1bd7a2Ssjelinek * that we must do the appropriate checking to see if the 'in use' 3373e1bd7a2Ssjelinek * details require a bit of additional work. 3383e1bd7a2Ssjelinek */ 3393e1bd7a2Ssjelinek 3403e1bd7a2Ssjelinek dm_get_slices(cur_disk_path, &slices, &error); 3413e1bd7a2Ssjelinek if (error) { 342*2b237d4bSny155746 /* 343*2b237d4bSny155746 * If ENODEV, it actually means the device is not in use. 344*2b237d4bSny155746 * We will return 0 without displaying error. 345*2b237d4bSny155746 */ 346*2b237d4bSny155746 if (error != ENODEV) { 347*2b237d4bSny155746 err_print("Error occurred with device in use" 348*2b237d4bSny155746 "checking: %s\n", strerror(error)); 3493e1bd7a2Ssjelinek return (found); 3503e1bd7a2Ssjelinek } 351*2b237d4bSny155746 } 3523e1bd7a2Ssjelinek if (slices == NULL) 3533e1bd7a2Ssjelinek return (found); 3543e1bd7a2Ssjelinek 3553e1bd7a2Ssjelinek for (i = 0; slices[i] != NULL; i++) { 3563e1bd7a2Ssjelinek /* 3573e1bd7a2Ssjelinek * If we are checking the whole disk 3583e1bd7a2Ssjelinek * then any and all in use data is 3593e1bd7a2Ssjelinek * relevant. 3603e1bd7a2Ssjelinek */ 3613e1bd7a2Ssjelinek if (start == UINT_MAX64) { 3623e1bd7a2Ssjelinek name = dm_get_name(slices[i], &error); 3633e1bd7a2Ssjelinek if (error != 0 || !name) { 3643e1bd7a2Ssjelinek err_print("Error occurred with device " 3653e1bd7a2Ssjelinek "in use checking: %s\n", 3663e1bd7a2Ssjelinek strerror(error)); 3673e1bd7a2Ssjelinek continue; 3683e1bd7a2Ssjelinek } 3693e1bd7a2Ssjelinek if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) || 3703e1bd7a2Ssjelinek error) { 3713e1bd7a2Ssjelinek if (error != 0) { 3723e1bd7a2Ssjelinek dm_free_name(name); 3733e1bd7a2Ssjelinek name = NULL; 3743e1bd7a2Ssjelinek err_print("Error occurred with device " 3753e1bd7a2Ssjelinek "in use checking: %s\n", 3763e1bd7a2Ssjelinek strerror(error)); 3773e1bd7a2Ssjelinek continue; 3783e1bd7a2Ssjelinek } 3793e1bd7a2Ssjelinek dm_free_name(name); 3803e1bd7a2Ssjelinek name = NULL; 3813e1bd7a2Ssjelinek /* 3823e1bd7a2Ssjelinek * If this is a dump device, then it is 3833e1bd7a2Ssjelinek * a failure. You cannot format a slice 3843e1bd7a2Ssjelinek * that is a dedicated dump device. 3853e1bd7a2Ssjelinek */ 3863e1bd7a2Ssjelinek 3873e1bd7a2Ssjelinek if (strstr(usage, DM_USE_DUMP)) { 3883e1bd7a2Ssjelinek if (print) { 3893e1bd7a2Ssjelinek err_print(usage); 3903e1bd7a2Ssjelinek free(usage); 3913e1bd7a2Ssjelinek } 3923e1bd7a2Ssjelinek dm_free_descriptors(slices); 3933e1bd7a2Ssjelinek return (1); 3943e1bd7a2Ssjelinek } 3953e1bd7a2Ssjelinek /* 3963e1bd7a2Ssjelinek * We really found a device that is in use. 3973e1bd7a2Ssjelinek * Set 'found' for the return value, and set 3983e1bd7a2Ssjelinek * 'check' to indicate below that we must 3993e1bd7a2Ssjelinek * get the partition number to set bm_inuse 4003e1bd7a2Ssjelinek * in the event we are trying to label this 4013e1bd7a2Ssjelinek * device. check_label is set when we are 4023e1bd7a2Ssjelinek * checking modifications for in use slices 4033e1bd7a2Ssjelinek * on the device. 4043e1bd7a2Ssjelinek */ 4053e1bd7a2Ssjelinek found ++; 4063e1bd7a2Ssjelinek check = 1; 4073e1bd7a2Ssjelinek if (print) { 4083e1bd7a2Ssjelinek err_print(usage); 4093e1bd7a2Ssjelinek free(usage); 4103e1bd7a2Ssjelinek } 4113e1bd7a2Ssjelinek } 4123e1bd7a2Ssjelinek } else { 4133e1bd7a2Ssjelinek /* 4143e1bd7a2Ssjelinek * Before getting the in use data, verify that the 4153e1bd7a2Ssjelinek * current slice is within the range we are checking. 4163e1bd7a2Ssjelinek */ 4173e1bd7a2Ssjelinek attrs = dm_get_attributes(slices[i], &error); 4183e1bd7a2Ssjelinek if (error) { 4193e1bd7a2Ssjelinek err_print("Error occurred with device in use " 4203e1bd7a2Ssjelinek "checking: %s\n", strerror(error)); 4213e1bd7a2Ssjelinek continue; 4223e1bd7a2Ssjelinek } 4233e1bd7a2Ssjelinek if (attrs == NULL) { 4243e1bd7a2Ssjelinek continue; 4253e1bd7a2Ssjelinek } 4263e1bd7a2Ssjelinek 4273e1bd7a2Ssjelinek (void) nvlist_lookup_uint64(attrs, DM_START, 4283e1bd7a2Ssjelinek &slice_start); 4293e1bd7a2Ssjelinek (void) nvlist_lookup_uint64(attrs, DM_SIZE, 4303e1bd7a2Ssjelinek &slice_size); 4313e1bd7a2Ssjelinek if (start >= (slice_start + slice_size) || 4323e1bd7a2Ssjelinek (end < slice_start)) { 4333e1bd7a2Ssjelinek nvlist_free(attrs); 4343e1bd7a2Ssjelinek attrs = NULL; 4353e1bd7a2Ssjelinek continue; 4363e1bd7a2Ssjelinek } 4373e1bd7a2Ssjelinek name = dm_get_name(slices[i], &error); 4383e1bd7a2Ssjelinek if (error != 0 || !name) { 4393e1bd7a2Ssjelinek err_print("Error occurred with device " 4403e1bd7a2Ssjelinek "in use checking: %s\n", 4413e1bd7a2Ssjelinek strerror(error)); 4423e1bd7a2Ssjelinek nvlist_free(attrs); 4433e1bd7a2Ssjelinek attrs = NULL; 4443e1bd7a2Ssjelinek continue; 4453e1bd7a2Ssjelinek } 4463e1bd7a2Ssjelinek if (dm_inuse(name, &usage, 4473e1bd7a2Ssjelinek DM_WHO_FORMAT, &error) || error) { 4483e1bd7a2Ssjelinek if (error != 0) { 4493e1bd7a2Ssjelinek dm_free_name(name); 4503e1bd7a2Ssjelinek name = NULL; 4513e1bd7a2Ssjelinek err_print("Error occurred with device " 4523e1bd7a2Ssjelinek "in use checking: %s\n", 4533e1bd7a2Ssjelinek strerror(error)); 4543e1bd7a2Ssjelinek nvlist_free(attrs); 4553e1bd7a2Ssjelinek attrs = NULL; 4563e1bd7a2Ssjelinek continue; 4573e1bd7a2Ssjelinek } 4583e1bd7a2Ssjelinek dm_free_name(name); 4593e1bd7a2Ssjelinek name = NULL; 4603e1bd7a2Ssjelinek /* 4613e1bd7a2Ssjelinek * If this is a dump device, then it is 4623e1bd7a2Ssjelinek * a failure. You cannot format a slice 4633e1bd7a2Ssjelinek * that is a dedicated dump device. 4643e1bd7a2Ssjelinek */ 4653e1bd7a2Ssjelinek if (strstr(usage, DM_USE_DUMP)) { 4663e1bd7a2Ssjelinek if (print) { 4673e1bd7a2Ssjelinek err_print(usage); 4683e1bd7a2Ssjelinek free(usage); 4693e1bd7a2Ssjelinek } 4703e1bd7a2Ssjelinek dm_free_descriptors(slices); 4713e1bd7a2Ssjelinek nvlist_free(attrs); 4723e1bd7a2Ssjelinek return (1); 4733e1bd7a2Ssjelinek } 4743e1bd7a2Ssjelinek /* 4753e1bd7a2Ssjelinek * We really found a device that is in use. 4763e1bd7a2Ssjelinek * Set 'found' for the return value, and set 4773e1bd7a2Ssjelinek * 'check' to indicate below that we must 4783e1bd7a2Ssjelinek * get the partition number to set bm_inuse 4793e1bd7a2Ssjelinek * in the event we are trying to label this 4803e1bd7a2Ssjelinek * device. check_label is set when we are 4813e1bd7a2Ssjelinek * checking modifications for in use slices 4823e1bd7a2Ssjelinek * on the device. 4833e1bd7a2Ssjelinek */ 4843e1bd7a2Ssjelinek found ++; 4853e1bd7a2Ssjelinek check = 1; 4863e1bd7a2Ssjelinek if (print) { 4873e1bd7a2Ssjelinek err_print(usage); 4883e1bd7a2Ssjelinek free(usage); 4893e1bd7a2Ssjelinek } 4903e1bd7a2Ssjelinek } 4913e1bd7a2Ssjelinek } 4923e1bd7a2Ssjelinek /* 4933e1bd7a2Ssjelinek * If check is set it means we found a slice(the current slice) 4943e1bd7a2Ssjelinek * on this device in use in some way. We potentially want 4953e1bd7a2Ssjelinek * to check this slice when labeling is 4963e1bd7a2Ssjelinek * requested. We set bm_inuse with this partition value 4973e1bd7a2Ssjelinek * for use later if check_label was set when called. 4983e1bd7a2Ssjelinek */ 4993e1bd7a2Ssjelinek if (check) { 5003e1bd7a2Ssjelinek name = dm_get_name(slices[i], &error); 5013e1bd7a2Ssjelinek if (error != 0 || !name) { 5023e1bd7a2Ssjelinek err_print("Error occurred with device " 5033e1bd7a2Ssjelinek "in use checking: %s\n", 5043e1bd7a2Ssjelinek strerror(error)); 5053e1bd7a2Ssjelinek nvlist_free(attrs); 5063e1bd7a2Ssjelinek attrs = NULL; 5073e1bd7a2Ssjelinek continue; 5083e1bd7a2Ssjelinek } 5093e1bd7a2Ssjelinek part = getpartition(name); 5103e1bd7a2Ssjelinek dm_free_name(name); 5113e1bd7a2Ssjelinek name = NULL; 5123e1bd7a2Ssjelinek if (part != -1) { 5133e1bd7a2Ssjelinek bm_inuse |= 1 << part; 5143e1bd7a2Ssjelinek } 5153e1bd7a2Ssjelinek check = 0; 5163e1bd7a2Ssjelinek } 5173e1bd7a2Ssjelinek /* 5183e1bd7a2Ssjelinek * If we have attributes then we have successfully 5193e1bd7a2Ssjelinek * found the slice we were looking for and we also 5203e1bd7a2Ssjelinek * know this means we are not searching the whole 5213e1bd7a2Ssjelinek * disk so break out of the loop 5223e1bd7a2Ssjelinek * now. 5233e1bd7a2Ssjelinek */ 5243e1bd7a2Ssjelinek if (attrs) { 5253e1bd7a2Ssjelinek nvlist_free(attrs); 5263e1bd7a2Ssjelinek break; 5273e1bd7a2Ssjelinek } 5283e1bd7a2Ssjelinek } 5293e1bd7a2Ssjelinek 5303e1bd7a2Ssjelinek if (slices) { 5313e1bd7a2Ssjelinek dm_free_descriptors(slices); 5323e1bd7a2Ssjelinek } 5333e1bd7a2Ssjelinek 5343e1bd7a2Ssjelinek /* 5353e1bd7a2Ssjelinek * The user is trying to label the disk. We have to do special 5363e1bd7a2Ssjelinek * checking here to ensure they are not trying to modify a slice 5373e1bd7a2Ssjelinek * that is in use in an incompatible way. 5383e1bd7a2Ssjelinek */ 5393e1bd7a2Ssjelinek if (check_label && bm_inuse) { 5403e1bd7a2Ssjelinek /* 5413e1bd7a2Ssjelinek * !0 indicates that we found a 5423e1bd7a2Ssjelinek * problem. In this case, we have overloaded 5433e1bd7a2Ssjelinek * the use of checkpartitions to work for 5443e1bd7a2Ssjelinek * in use devices. bm_inuse is representative 5453e1bd7a2Ssjelinek * of the slice that is in use, not that 5463e1bd7a2Ssjelinek * is mounted as is in the case of the normal 5473e1bd7a2Ssjelinek * use of checkpartitions. 5483e1bd7a2Ssjelinek * 5493e1bd7a2Ssjelinek * The call to checkpartitions will return !0 if 5503e1bd7a2Ssjelinek * we are trying to shrink a device that we have found 5513e1bd7a2Ssjelinek * to be in use above. 5523e1bd7a2Ssjelinek */ 5533e1bd7a2Ssjelinek return (checkpartitions(bm_inuse)); 5543e1bd7a2Ssjelinek } 5553e1bd7a2Ssjelinek 5563e1bd7a2Ssjelinek return (found); 5573e1bd7a2Ssjelinek } 5583e1bd7a2Ssjelinek /* 5593e1bd7a2Ssjelinek * This routine checks to see if there are mounted partitions overlapping 5603e1bd7a2Ssjelinek * a given portion of a disk. If the start parameter is < 0, it means 5613e1bd7a2Ssjelinek * that the entire disk should be checked. 5623e1bd7a2Ssjelinek */ 5633e1bd7a2Ssjelinek int 5643e1bd7a2Ssjelinek checkmount(start, end) 5653e1bd7a2Ssjelinek diskaddr_t start, end; 5663e1bd7a2Ssjelinek { 5673e1bd7a2Ssjelinek FILE *fp; 5683e1bd7a2Ssjelinek int found = 0; 5693e1bd7a2Ssjelinek struct dk_map32 *map; 5703e1bd7a2Ssjelinek int part; 5713e1bd7a2Ssjelinek struct mnttab mnt_record; 5723e1bd7a2Ssjelinek struct mnttab *mp = &mnt_record; 5733e1bd7a2Ssjelinek 5743e1bd7a2Ssjelinek /* 5753e1bd7a2Ssjelinek * If we are only checking part of the disk, the disk must 5763e1bd7a2Ssjelinek * have a partition map to check against. If it doesn't, 5773e1bd7a2Ssjelinek * we hope for the best. 5783e1bd7a2Ssjelinek */ 5793e1bd7a2Ssjelinek if (cur_parts == NULL) 5803e1bd7a2Ssjelinek return (0); 5813e1bd7a2Ssjelinek 5823e1bd7a2Ssjelinek /* 5833e1bd7a2Ssjelinek * Lock out interrupts because of the mntent protocol. 5843e1bd7a2Ssjelinek */ 5853e1bd7a2Ssjelinek enter_critical(); 5863e1bd7a2Ssjelinek /* 5873e1bd7a2Ssjelinek * Open the mount table. 5883e1bd7a2Ssjelinek */ 5893e1bd7a2Ssjelinek fp = fopen(MNTTAB, "r"); 5903e1bd7a2Ssjelinek if (fp == NULL) { 5913e1bd7a2Ssjelinek err_print("Unable to open mount table.\n"); 5923e1bd7a2Ssjelinek fullabort(); 5933e1bd7a2Ssjelinek } 5943e1bd7a2Ssjelinek /* 5953e1bd7a2Ssjelinek * Loop through the mount table until we run out of entries. 5963e1bd7a2Ssjelinek */ 5973e1bd7a2Ssjelinek while ((getmntent(fp, mp)) != -1) { 5983e1bd7a2Ssjelinek 5993e1bd7a2Ssjelinek if ((part = getpartition(mp->mnt_special)) == -1) 6003e1bd7a2Ssjelinek continue; 6013e1bd7a2Ssjelinek 6023e1bd7a2Ssjelinek /* 6033e1bd7a2Ssjelinek * It's a mount on the disk we're checking. If we are 6043e1bd7a2Ssjelinek * checking whole disk, then we found trouble. We can 6053e1bd7a2Ssjelinek * quit searching. 6063e1bd7a2Ssjelinek */ 6073e1bd7a2Ssjelinek if (start == UINT_MAX64) { 6083e1bd7a2Ssjelinek found = -1; 6093e1bd7a2Ssjelinek break; 6103e1bd7a2Ssjelinek } 6113e1bd7a2Ssjelinek 6123e1bd7a2Ssjelinek /* 6133e1bd7a2Ssjelinek * If the partition overlaps the zone we're checking, 6143e1bd7a2Ssjelinek * then we found trouble. We can quit searching. 6153e1bd7a2Ssjelinek */ 6163e1bd7a2Ssjelinek map = &cur_parts->pinfo_map[part]; 6173e1bd7a2Ssjelinek if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) || 6183e1bd7a2Ssjelinek (end < (int)(map->dkl_cylno * spc()))) { 6193e1bd7a2Ssjelinek continue; 6203e1bd7a2Ssjelinek } 6213e1bd7a2Ssjelinek found = -1; 6223e1bd7a2Ssjelinek break; 6233e1bd7a2Ssjelinek } 6243e1bd7a2Ssjelinek /* 6253e1bd7a2Ssjelinek * Close down the mount table. 6263e1bd7a2Ssjelinek */ 6273e1bd7a2Ssjelinek (void) fclose(fp); 6283e1bd7a2Ssjelinek exit_critical(); 6293e1bd7a2Ssjelinek 6303e1bd7a2Ssjelinek /* 6313e1bd7a2Ssjelinek * If we found trouble and we're running from a command file, 6323e1bd7a2Ssjelinek * quit before doing something we really regret. 6333e1bd7a2Ssjelinek */ 6343e1bd7a2Ssjelinek 6353e1bd7a2Ssjelinek if (found && option_f) { 6363e1bd7a2Ssjelinek err_print("Operation on mounted disks must be interactive.\n"); 6373e1bd7a2Ssjelinek cmdabort(SIGINT); 6383e1bd7a2Ssjelinek } 6393e1bd7a2Ssjelinek /* 6403e1bd7a2Ssjelinek * Return the result. 6413e1bd7a2Ssjelinek */ 6423e1bd7a2Ssjelinek return (found); 6433e1bd7a2Ssjelinek } 6443e1bd7a2Ssjelinek 6453e1bd7a2Ssjelinek int 6463e1bd7a2Ssjelinek check_label_with_swap() 6473e1bd7a2Ssjelinek { 6483e1bd7a2Ssjelinek int i; 6493e1bd7a2Ssjelinek struct swaptable *st; 6503e1bd7a2Ssjelinek struct swapent *swapent; 6513e1bd7a2Ssjelinek int part; 6523e1bd7a2Ssjelinek int bm_swap = 0; 6533e1bd7a2Ssjelinek 6543e1bd7a2Ssjelinek /* 6553e1bd7a2Ssjelinek * If we are only checking part of the disk, the disk must 6563e1bd7a2Ssjelinek * have a partition map to check against. If it doesn't, 6573e1bd7a2Ssjelinek * we hope for the best. 6583e1bd7a2Ssjelinek */ 6593e1bd7a2Ssjelinek if (cur_parts == NULL) 6603e1bd7a2Ssjelinek return (0); /* Will be checked later */ 6613e1bd7a2Ssjelinek 6623e1bd7a2Ssjelinek /* 6633e1bd7a2Ssjelinek * Check for swap entries 6643e1bd7a2Ssjelinek */ 6653e1bd7a2Ssjelinek st = getswapentries(); 6663e1bd7a2Ssjelinek /* 6673e1bd7a2Ssjelinek * if there are no swap entries return. 6683e1bd7a2Ssjelinek */ 6693e1bd7a2Ssjelinek if (st == (struct swaptable *)NULL) 6703e1bd7a2Ssjelinek return (0); 6713e1bd7a2Ssjelinek swapent = st->swt_ent; 6723e1bd7a2Ssjelinek for (i = 0; i < st->swt_n; i++, swapent++) 6733e1bd7a2Ssjelinek if ((part = getpartition(swapent->ste_path)) != -1) 6743e1bd7a2Ssjelinek bm_swap |= (1 << part); 6753e1bd7a2Ssjelinek freeswapentries(st); 6763e1bd7a2Ssjelinek 6773e1bd7a2Ssjelinek return (checkpartitions(bm_swap)); 6783e1bd7a2Ssjelinek } 6793e1bd7a2Ssjelinek 6803e1bd7a2Ssjelinek /* 6813e1bd7a2Ssjelinek * Check the new label with the existing label on the disk, 6823e1bd7a2Ssjelinek * to make sure that any mounted partitions are not being 6833e1bd7a2Ssjelinek * affected by writing the new label. 6843e1bd7a2Ssjelinek */ 6853e1bd7a2Ssjelinek int 6863e1bd7a2Ssjelinek check_label_with_mount() 6873e1bd7a2Ssjelinek { 6883e1bd7a2Ssjelinek FILE *fp; 6893e1bd7a2Ssjelinek int part; 6903e1bd7a2Ssjelinek struct mnttab mnt_record; 6913e1bd7a2Ssjelinek struct mnttab *mp = &mnt_record; 6923e1bd7a2Ssjelinek int bm_mounted = 0; 6933e1bd7a2Ssjelinek 6943e1bd7a2Ssjelinek 6953e1bd7a2Ssjelinek /* 6963e1bd7a2Ssjelinek * If we are only checking part of the disk, the disk must 6973e1bd7a2Ssjelinek * have a partition map to check against. If it doesn't, 6983e1bd7a2Ssjelinek * we hope for the best. 6993e1bd7a2Ssjelinek */ 7003e1bd7a2Ssjelinek if (cur_parts == NULL) 7013e1bd7a2Ssjelinek return (0); /* Will be checked later */ 7023e1bd7a2Ssjelinek 7033e1bd7a2Ssjelinek /* 7043e1bd7a2Ssjelinek * Lock out interrupts because of the mntent protocol. 7053e1bd7a2Ssjelinek */ 7063e1bd7a2Ssjelinek enter_critical(); 7073e1bd7a2Ssjelinek /* 7083e1bd7a2Ssjelinek * Open the mount table. 7093e1bd7a2Ssjelinek */ 7103e1bd7a2Ssjelinek fp = fopen(MNTTAB, "r"); 7113e1bd7a2Ssjelinek if (fp == NULL) { 7123e1bd7a2Ssjelinek err_print("Unable to open mount table.\n"); 7133e1bd7a2Ssjelinek fullabort(); 7143e1bd7a2Ssjelinek } 7153e1bd7a2Ssjelinek /* 7163e1bd7a2Ssjelinek * Loop through the mount table until we run out of entries. 7173e1bd7a2Ssjelinek */ 7183e1bd7a2Ssjelinek while ((getmntent(fp, mp)) != -1) { 7193e1bd7a2Ssjelinek if ((part = getpartition(mp->mnt_special)) != -1) 7203e1bd7a2Ssjelinek bm_mounted |= (1 << part); 7213e1bd7a2Ssjelinek } 7223e1bd7a2Ssjelinek /* 7233e1bd7a2Ssjelinek * Close down the mount table. 7243e1bd7a2Ssjelinek */ 7253e1bd7a2Ssjelinek (void) fclose(fp); 7263e1bd7a2Ssjelinek exit_critical(); 7273e1bd7a2Ssjelinek 7283e1bd7a2Ssjelinek return (checkpartitions(bm_mounted)); 7293e1bd7a2Ssjelinek 7303e1bd7a2Ssjelinek } 7313e1bd7a2Ssjelinek 7323e1bd7a2Ssjelinek /* 7333e1bd7a2Ssjelinek * This Routine checks if any partitions specified 7343e1bd7a2Ssjelinek * are affected by writing the new label 7353e1bd7a2Ssjelinek */ 7363e1bd7a2Ssjelinek static int 7373e1bd7a2Ssjelinek checkpartitions(int bm_mounted) 7383e1bd7a2Ssjelinek { 7393e1bd7a2Ssjelinek struct dk_map32 *n; 7403e1bd7a2Ssjelinek struct dk_map *o; 7413e1bd7a2Ssjelinek struct dk_allmap old_map; 7423e1bd7a2Ssjelinek int i, found = 0; 7433e1bd7a2Ssjelinek 7443e1bd7a2Ssjelinek /* 7453e1bd7a2Ssjelinek * Now we need to check that the current partition list and the 7463e1bd7a2Ssjelinek * previous partition list (which there must be if we actually 7473e1bd7a2Ssjelinek * have partitions mounted) overlap in any way on the mounted 7483e1bd7a2Ssjelinek * partitions 7493e1bd7a2Ssjelinek */ 7503e1bd7a2Ssjelinek 7513e1bd7a2Ssjelinek /* 7523e1bd7a2Ssjelinek * Get the "real" (on-disk) version of the partition table 7533e1bd7a2Ssjelinek */ 7543e1bd7a2Ssjelinek if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) { 7553e1bd7a2Ssjelinek err_print("Unable to get current partition map.\n"); 7563e1bd7a2Ssjelinek return (-1); 7573e1bd7a2Ssjelinek } 7583e1bd7a2Ssjelinek for (i = 0; i < NDKMAP; i++) { 7593e1bd7a2Ssjelinek if (bm_mounted & (1 << i)) { 7603e1bd7a2Ssjelinek /* 7613e1bd7a2Ssjelinek * This partition is mounted 7623e1bd7a2Ssjelinek */ 7633e1bd7a2Ssjelinek o = &old_map.dka_map[i]; 7643e1bd7a2Ssjelinek n = &cur_parts->pinfo_map[i]; 7653e1bd7a2Ssjelinek #ifdef DEBUG 7663e1bd7a2Ssjelinek fmt_print( 7673e1bd7a2Ssjelinek "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE); 7683e1bd7a2Ssjelinek #endif 7693e1bd7a2Ssjelinek /* 7703e1bd7a2Ssjelinek * If partition is identical, we're fine. 7713e1bd7a2Ssjelinek * If the partition grows, we're also fine, because 7723e1bd7a2Ssjelinek * the routines in partition.c check for overflow. 7733e1bd7a2Ssjelinek * It will (ultimately) be up to the routines in 7743e1bd7a2Ssjelinek * partition.c to warn about creation of overlapping 7753e1bd7a2Ssjelinek * partitions 7763e1bd7a2Ssjelinek */ 7773e1bd7a2Ssjelinek if (o->dkl_cylno == n->dkl_cylno && 7783e1bd7a2Ssjelinek o->dkl_nblk <= n->dkl_nblk) { 7793e1bd7a2Ssjelinek #ifdef DEBUG 7803e1bd7a2Ssjelinek if (o->dkl_nblk < n->dkl_nblk) { 7813e1bd7a2Ssjelinek fmt_print( 7823e1bd7a2Ssjelinek "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk); 7833e1bd7a2Ssjelinek } 7843e1bd7a2Ssjelinek fmt_print("\n"); 7853e1bd7a2Ssjelinek #endif 7863e1bd7a2Ssjelinek continue; 7873e1bd7a2Ssjelinek } 7883e1bd7a2Ssjelinek #ifdef DEBUG 7893e1bd7a2Ssjelinek fmt_print("- changes; old (%d,%d)->new (%d,%d)\n", 7903e1bd7a2Ssjelinek o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, 7913e1bd7a2Ssjelinek n->dkl_nblk); 7923e1bd7a2Ssjelinek #endif 7933e1bd7a2Ssjelinek found = -1; 7943e1bd7a2Ssjelinek } 7953e1bd7a2Ssjelinek if (found) 7963e1bd7a2Ssjelinek break; 7973e1bd7a2Ssjelinek } 7983e1bd7a2Ssjelinek 7993e1bd7a2Ssjelinek /* 8003e1bd7a2Ssjelinek * If we found trouble and we're running from a command file, 8013e1bd7a2Ssjelinek * quit before doing something we really regret. 8023e1bd7a2Ssjelinek */ 8033e1bd7a2Ssjelinek 8043e1bd7a2Ssjelinek if (found && option_f) { 8053e1bd7a2Ssjelinek err_print("Operation on mounted disks or \ 8063e1bd7a2Ssjelinek disks currently being used for swapping must be interactive.\n"); 8073e1bd7a2Ssjelinek cmdabort(SIGINT); 8083e1bd7a2Ssjelinek } 8093e1bd7a2Ssjelinek /* 8103e1bd7a2Ssjelinek * Return the result. 8113e1bd7a2Ssjelinek */ 8123e1bd7a2Ssjelinek return (found); 8133e1bd7a2Ssjelinek } 814