13e1bd7a2Ssjelinek /* 23e1bd7a2Ssjelinek * CDDL HEADER START 33e1bd7a2Ssjelinek * 43e1bd7a2Ssjelinek * The contents of this file are subject to the terms of the 5*d25b227dSzl149053 * Common Development and Distribution License (the "License"). 6*d25b227dSzl149053 * 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*d25b227dSzl149053 * Copyright 2006 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> 44*d25b227dSzl149053 #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> 50*d25b227dSzl149053 #include <sys/mkdev.h> 51*d25b227dSzl149053 #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 /* 299*d25b227dSzl149053 * Skip if it is not a real disk 300*d25b227dSzl149053 * 301*d25b227dSzl149053 * There could be two kinds of strings in cur_disk_path 302*d25b227dSzl149053 * One starts with c?t?d?, while the other is a absolute path of a 303*d25b227dSzl149053 * block device file. 304*d25b227dSzl149053 */ 305*d25b227dSzl149053 306*d25b227dSzl149053 if (*cur_disk_path != 'c') { 307*d25b227dSzl149053 struct stat stbuf; 308*d25b227dSzl149053 char majorname[16]; 309*d25b227dSzl149053 major_t majornum; 310*d25b227dSzl149053 311*d25b227dSzl149053 (void) stat(cur_disk_path, &stbuf); 312*d25b227dSzl149053 majornum = major(stbuf.st_rdev); 313*d25b227dSzl149053 (void) modctl(MODGETNAME, majorname, sizeof (majorname), 314*d25b227dSzl149053 &majornum); 315*d25b227dSzl149053 316*d25b227dSzl149053 if (strcmp(majorname, "sd")) 317*d25b227dSzl149053 if (strcmp(majorname, "ssd")) 318*d25b227dSzl149053 if (strcmp(majorname, "cmdk")) 319*d25b227dSzl149053 return (0); 320*d25b227dSzl149053 } 321*d25b227dSzl149053 322*d25b227dSzl149053 /* 323*d25b227dSzl149053 * Truncate the characters following "d*", such as "s*" or "p*" 324*d25b227dSzl149053 */ 325*d25b227dSzl149053 cur_disk_path = basename(cur_disk_path); 326*d25b227dSzl149053 name = strrchr(cur_disk_path, 'd'); 327*d25b227dSzl149053 if (name) { 328*d25b227dSzl149053 name++; 329*d25b227dSzl149053 for (; (*name <= '9') && (*name >= '0'); name++); 330*d25b227dSzl149053 *name = (char)0; 331*d25b227dSzl149053 } 332*d25b227dSzl149053 333*d25b227dSzl149053 334*d25b227dSzl149053 /* 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) { 3423e1bd7a2Ssjelinek err_print("Error occurred with device in use checking: %s\n", 3433e1bd7a2Ssjelinek strerror(error)); 3443e1bd7a2Ssjelinek return (found); 3453e1bd7a2Ssjelinek } 3463e1bd7a2Ssjelinek if (slices == NULL) 3473e1bd7a2Ssjelinek return (found); 3483e1bd7a2Ssjelinek 3493e1bd7a2Ssjelinek for (i = 0; slices[i] != NULL; i++) { 3503e1bd7a2Ssjelinek /* 3513e1bd7a2Ssjelinek * If we are checking the whole disk 3523e1bd7a2Ssjelinek * then any and all in use data is 3533e1bd7a2Ssjelinek * relevant. 3543e1bd7a2Ssjelinek */ 3553e1bd7a2Ssjelinek if (start == UINT_MAX64) { 3563e1bd7a2Ssjelinek name = dm_get_name(slices[i], &error); 3573e1bd7a2Ssjelinek if (error != 0 || !name) { 3583e1bd7a2Ssjelinek err_print("Error occurred with device " 3593e1bd7a2Ssjelinek "in use checking: %s\n", 3603e1bd7a2Ssjelinek strerror(error)); 3613e1bd7a2Ssjelinek continue; 3623e1bd7a2Ssjelinek } 3633e1bd7a2Ssjelinek if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) || 3643e1bd7a2Ssjelinek error) { 3653e1bd7a2Ssjelinek if (error != 0) { 3663e1bd7a2Ssjelinek dm_free_name(name); 3673e1bd7a2Ssjelinek name = NULL; 3683e1bd7a2Ssjelinek err_print("Error occurred with device " 3693e1bd7a2Ssjelinek "in use checking: %s\n", 3703e1bd7a2Ssjelinek strerror(error)); 3713e1bd7a2Ssjelinek continue; 3723e1bd7a2Ssjelinek } 3733e1bd7a2Ssjelinek dm_free_name(name); 3743e1bd7a2Ssjelinek name = NULL; 3753e1bd7a2Ssjelinek /* 3763e1bd7a2Ssjelinek * If this is a dump device, then it is 3773e1bd7a2Ssjelinek * a failure. You cannot format a slice 3783e1bd7a2Ssjelinek * that is a dedicated dump device. 3793e1bd7a2Ssjelinek */ 3803e1bd7a2Ssjelinek 3813e1bd7a2Ssjelinek if (strstr(usage, DM_USE_DUMP)) { 3823e1bd7a2Ssjelinek if (print) { 3833e1bd7a2Ssjelinek err_print(usage); 3843e1bd7a2Ssjelinek free(usage); 3853e1bd7a2Ssjelinek } 3863e1bd7a2Ssjelinek dm_free_descriptors(slices); 3873e1bd7a2Ssjelinek return (1); 3883e1bd7a2Ssjelinek } 3893e1bd7a2Ssjelinek /* 3903e1bd7a2Ssjelinek * We really found a device that is in use. 3913e1bd7a2Ssjelinek * Set 'found' for the return value, and set 3923e1bd7a2Ssjelinek * 'check' to indicate below that we must 3933e1bd7a2Ssjelinek * get the partition number to set bm_inuse 3943e1bd7a2Ssjelinek * in the event we are trying to label this 3953e1bd7a2Ssjelinek * device. check_label is set when we are 3963e1bd7a2Ssjelinek * checking modifications for in use slices 3973e1bd7a2Ssjelinek * on the device. 3983e1bd7a2Ssjelinek */ 3993e1bd7a2Ssjelinek found ++; 4003e1bd7a2Ssjelinek check = 1; 4013e1bd7a2Ssjelinek if (print) { 4023e1bd7a2Ssjelinek err_print(usage); 4033e1bd7a2Ssjelinek free(usage); 4043e1bd7a2Ssjelinek } 4053e1bd7a2Ssjelinek } 4063e1bd7a2Ssjelinek } else { 4073e1bd7a2Ssjelinek /* 4083e1bd7a2Ssjelinek * Before getting the in use data, verify that the 4093e1bd7a2Ssjelinek * current slice is within the range we are checking. 4103e1bd7a2Ssjelinek */ 4113e1bd7a2Ssjelinek attrs = dm_get_attributes(slices[i], &error); 4123e1bd7a2Ssjelinek if (error) { 4133e1bd7a2Ssjelinek err_print("Error occurred with device in use " 4143e1bd7a2Ssjelinek "checking: %s\n", strerror(error)); 4153e1bd7a2Ssjelinek continue; 4163e1bd7a2Ssjelinek } 4173e1bd7a2Ssjelinek if (attrs == NULL) { 4183e1bd7a2Ssjelinek continue; 4193e1bd7a2Ssjelinek } 4203e1bd7a2Ssjelinek 4213e1bd7a2Ssjelinek (void) nvlist_lookup_uint64(attrs, DM_START, 4223e1bd7a2Ssjelinek &slice_start); 4233e1bd7a2Ssjelinek (void) nvlist_lookup_uint64(attrs, DM_SIZE, 4243e1bd7a2Ssjelinek &slice_size); 4253e1bd7a2Ssjelinek if (start >= (slice_start + slice_size) || 4263e1bd7a2Ssjelinek (end < slice_start)) { 4273e1bd7a2Ssjelinek nvlist_free(attrs); 4283e1bd7a2Ssjelinek attrs = NULL; 4293e1bd7a2Ssjelinek continue; 4303e1bd7a2Ssjelinek } 4313e1bd7a2Ssjelinek name = dm_get_name(slices[i], &error); 4323e1bd7a2Ssjelinek if (error != 0 || !name) { 4333e1bd7a2Ssjelinek err_print("Error occurred with device " 4343e1bd7a2Ssjelinek "in use checking: %s\n", 4353e1bd7a2Ssjelinek strerror(error)); 4363e1bd7a2Ssjelinek nvlist_free(attrs); 4373e1bd7a2Ssjelinek attrs = NULL; 4383e1bd7a2Ssjelinek continue; 4393e1bd7a2Ssjelinek } 4403e1bd7a2Ssjelinek if (dm_inuse(name, &usage, 4413e1bd7a2Ssjelinek DM_WHO_FORMAT, &error) || error) { 4423e1bd7a2Ssjelinek if (error != 0) { 4433e1bd7a2Ssjelinek dm_free_name(name); 4443e1bd7a2Ssjelinek name = NULL; 4453e1bd7a2Ssjelinek err_print("Error occurred with device " 4463e1bd7a2Ssjelinek "in use checking: %s\n", 4473e1bd7a2Ssjelinek strerror(error)); 4483e1bd7a2Ssjelinek nvlist_free(attrs); 4493e1bd7a2Ssjelinek attrs = NULL; 4503e1bd7a2Ssjelinek continue; 4513e1bd7a2Ssjelinek } 4523e1bd7a2Ssjelinek dm_free_name(name); 4533e1bd7a2Ssjelinek name = NULL; 4543e1bd7a2Ssjelinek /* 4553e1bd7a2Ssjelinek * If this is a dump device, then it is 4563e1bd7a2Ssjelinek * a failure. You cannot format a slice 4573e1bd7a2Ssjelinek * that is a dedicated dump device. 4583e1bd7a2Ssjelinek */ 4593e1bd7a2Ssjelinek if (strstr(usage, DM_USE_DUMP)) { 4603e1bd7a2Ssjelinek if (print) { 4613e1bd7a2Ssjelinek err_print(usage); 4623e1bd7a2Ssjelinek free(usage); 4633e1bd7a2Ssjelinek } 4643e1bd7a2Ssjelinek dm_free_descriptors(slices); 4653e1bd7a2Ssjelinek nvlist_free(attrs); 4663e1bd7a2Ssjelinek return (1); 4673e1bd7a2Ssjelinek } 4683e1bd7a2Ssjelinek /* 4693e1bd7a2Ssjelinek * We really found a device that is in use. 4703e1bd7a2Ssjelinek * Set 'found' for the return value, and set 4713e1bd7a2Ssjelinek * 'check' to indicate below that we must 4723e1bd7a2Ssjelinek * get the partition number to set bm_inuse 4733e1bd7a2Ssjelinek * in the event we are trying to label this 4743e1bd7a2Ssjelinek * device. check_label is set when we are 4753e1bd7a2Ssjelinek * checking modifications for in use slices 4763e1bd7a2Ssjelinek * on the device. 4773e1bd7a2Ssjelinek */ 4783e1bd7a2Ssjelinek found ++; 4793e1bd7a2Ssjelinek check = 1; 4803e1bd7a2Ssjelinek if (print) { 4813e1bd7a2Ssjelinek err_print(usage); 4823e1bd7a2Ssjelinek free(usage); 4833e1bd7a2Ssjelinek } 4843e1bd7a2Ssjelinek } 4853e1bd7a2Ssjelinek } 4863e1bd7a2Ssjelinek /* 4873e1bd7a2Ssjelinek * If check is set it means we found a slice(the current slice) 4883e1bd7a2Ssjelinek * on this device in use in some way. We potentially want 4893e1bd7a2Ssjelinek * to check this slice when labeling is 4903e1bd7a2Ssjelinek * requested. We set bm_inuse with this partition value 4913e1bd7a2Ssjelinek * for use later if check_label was set when called. 4923e1bd7a2Ssjelinek */ 4933e1bd7a2Ssjelinek if (check) { 4943e1bd7a2Ssjelinek name = dm_get_name(slices[i], &error); 4953e1bd7a2Ssjelinek if (error != 0 || !name) { 4963e1bd7a2Ssjelinek err_print("Error occurred with device " 4973e1bd7a2Ssjelinek "in use checking: %s\n", 4983e1bd7a2Ssjelinek strerror(error)); 4993e1bd7a2Ssjelinek nvlist_free(attrs); 5003e1bd7a2Ssjelinek attrs = NULL; 5013e1bd7a2Ssjelinek continue; 5023e1bd7a2Ssjelinek } 5033e1bd7a2Ssjelinek part = getpartition(name); 5043e1bd7a2Ssjelinek dm_free_name(name); 5053e1bd7a2Ssjelinek name = NULL; 5063e1bd7a2Ssjelinek if (part != -1) { 5073e1bd7a2Ssjelinek bm_inuse |= 1 << part; 5083e1bd7a2Ssjelinek } 5093e1bd7a2Ssjelinek check = 0; 5103e1bd7a2Ssjelinek } 5113e1bd7a2Ssjelinek /* 5123e1bd7a2Ssjelinek * If we have attributes then we have successfully 5133e1bd7a2Ssjelinek * found the slice we were looking for and we also 5143e1bd7a2Ssjelinek * know this means we are not searching the whole 5153e1bd7a2Ssjelinek * disk so break out of the loop 5163e1bd7a2Ssjelinek * now. 5173e1bd7a2Ssjelinek */ 5183e1bd7a2Ssjelinek if (attrs) { 5193e1bd7a2Ssjelinek nvlist_free(attrs); 5203e1bd7a2Ssjelinek break; 5213e1bd7a2Ssjelinek } 5223e1bd7a2Ssjelinek } 5233e1bd7a2Ssjelinek 5243e1bd7a2Ssjelinek if (slices) { 5253e1bd7a2Ssjelinek dm_free_descriptors(slices); 5263e1bd7a2Ssjelinek } 5273e1bd7a2Ssjelinek 5283e1bd7a2Ssjelinek /* 5293e1bd7a2Ssjelinek * The user is trying to label the disk. We have to do special 5303e1bd7a2Ssjelinek * checking here to ensure they are not trying to modify a slice 5313e1bd7a2Ssjelinek * that is in use in an incompatible way. 5323e1bd7a2Ssjelinek */ 5333e1bd7a2Ssjelinek if (check_label && bm_inuse) { 5343e1bd7a2Ssjelinek /* 5353e1bd7a2Ssjelinek * !0 indicates that we found a 5363e1bd7a2Ssjelinek * problem. In this case, we have overloaded 5373e1bd7a2Ssjelinek * the use of checkpartitions to work for 5383e1bd7a2Ssjelinek * in use devices. bm_inuse is representative 5393e1bd7a2Ssjelinek * of the slice that is in use, not that 5403e1bd7a2Ssjelinek * is mounted as is in the case of the normal 5413e1bd7a2Ssjelinek * use of checkpartitions. 5423e1bd7a2Ssjelinek * 5433e1bd7a2Ssjelinek * The call to checkpartitions will return !0 if 5443e1bd7a2Ssjelinek * we are trying to shrink a device that we have found 5453e1bd7a2Ssjelinek * to be in use above. 5463e1bd7a2Ssjelinek */ 5473e1bd7a2Ssjelinek return (checkpartitions(bm_inuse)); 5483e1bd7a2Ssjelinek } 5493e1bd7a2Ssjelinek 5503e1bd7a2Ssjelinek return (found); 5513e1bd7a2Ssjelinek } 5523e1bd7a2Ssjelinek /* 5533e1bd7a2Ssjelinek * This routine checks to see if there are mounted partitions overlapping 5543e1bd7a2Ssjelinek * a given portion of a disk. If the start parameter is < 0, it means 5553e1bd7a2Ssjelinek * that the entire disk should be checked. 5563e1bd7a2Ssjelinek */ 5573e1bd7a2Ssjelinek int 5583e1bd7a2Ssjelinek checkmount(start, end) 5593e1bd7a2Ssjelinek diskaddr_t start, end; 5603e1bd7a2Ssjelinek { 5613e1bd7a2Ssjelinek FILE *fp; 5623e1bd7a2Ssjelinek int found = 0; 5633e1bd7a2Ssjelinek struct dk_map32 *map; 5643e1bd7a2Ssjelinek int part; 5653e1bd7a2Ssjelinek struct mnttab mnt_record; 5663e1bd7a2Ssjelinek struct mnttab *mp = &mnt_record; 5673e1bd7a2Ssjelinek 5683e1bd7a2Ssjelinek /* 5693e1bd7a2Ssjelinek * If we are only checking part of the disk, the disk must 5703e1bd7a2Ssjelinek * have a partition map to check against. If it doesn't, 5713e1bd7a2Ssjelinek * we hope for the best. 5723e1bd7a2Ssjelinek */ 5733e1bd7a2Ssjelinek if (cur_parts == NULL) 5743e1bd7a2Ssjelinek return (0); 5753e1bd7a2Ssjelinek 5763e1bd7a2Ssjelinek /* 5773e1bd7a2Ssjelinek * Lock out interrupts because of the mntent protocol. 5783e1bd7a2Ssjelinek */ 5793e1bd7a2Ssjelinek enter_critical(); 5803e1bd7a2Ssjelinek /* 5813e1bd7a2Ssjelinek * Open the mount table. 5823e1bd7a2Ssjelinek */ 5833e1bd7a2Ssjelinek fp = fopen(MNTTAB, "r"); 5843e1bd7a2Ssjelinek if (fp == NULL) { 5853e1bd7a2Ssjelinek err_print("Unable to open mount table.\n"); 5863e1bd7a2Ssjelinek fullabort(); 5873e1bd7a2Ssjelinek } 5883e1bd7a2Ssjelinek /* 5893e1bd7a2Ssjelinek * Loop through the mount table until we run out of entries. 5903e1bd7a2Ssjelinek */ 5913e1bd7a2Ssjelinek while ((getmntent(fp, mp)) != -1) { 5923e1bd7a2Ssjelinek 5933e1bd7a2Ssjelinek if ((part = getpartition(mp->mnt_special)) == -1) 5943e1bd7a2Ssjelinek continue; 5953e1bd7a2Ssjelinek 5963e1bd7a2Ssjelinek /* 5973e1bd7a2Ssjelinek * It's a mount on the disk we're checking. If we are 5983e1bd7a2Ssjelinek * checking whole disk, then we found trouble. We can 5993e1bd7a2Ssjelinek * quit searching. 6003e1bd7a2Ssjelinek */ 6013e1bd7a2Ssjelinek if (start == UINT_MAX64) { 6023e1bd7a2Ssjelinek found = -1; 6033e1bd7a2Ssjelinek break; 6043e1bd7a2Ssjelinek } 6053e1bd7a2Ssjelinek 6063e1bd7a2Ssjelinek /* 6073e1bd7a2Ssjelinek * If the partition overlaps the zone we're checking, 6083e1bd7a2Ssjelinek * then we found trouble. We can quit searching. 6093e1bd7a2Ssjelinek */ 6103e1bd7a2Ssjelinek map = &cur_parts->pinfo_map[part]; 6113e1bd7a2Ssjelinek if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) || 6123e1bd7a2Ssjelinek (end < (int)(map->dkl_cylno * spc()))) { 6133e1bd7a2Ssjelinek continue; 6143e1bd7a2Ssjelinek } 6153e1bd7a2Ssjelinek found = -1; 6163e1bd7a2Ssjelinek break; 6173e1bd7a2Ssjelinek } 6183e1bd7a2Ssjelinek /* 6193e1bd7a2Ssjelinek * Close down the mount table. 6203e1bd7a2Ssjelinek */ 6213e1bd7a2Ssjelinek (void) fclose(fp); 6223e1bd7a2Ssjelinek exit_critical(); 6233e1bd7a2Ssjelinek 6243e1bd7a2Ssjelinek /* 6253e1bd7a2Ssjelinek * If we found trouble and we're running from a command file, 6263e1bd7a2Ssjelinek * quit before doing something we really regret. 6273e1bd7a2Ssjelinek */ 6283e1bd7a2Ssjelinek 6293e1bd7a2Ssjelinek if (found && option_f) { 6303e1bd7a2Ssjelinek err_print("Operation on mounted disks must be interactive.\n"); 6313e1bd7a2Ssjelinek cmdabort(SIGINT); 6323e1bd7a2Ssjelinek } 6333e1bd7a2Ssjelinek /* 6343e1bd7a2Ssjelinek * Return the result. 6353e1bd7a2Ssjelinek */ 6363e1bd7a2Ssjelinek return (found); 6373e1bd7a2Ssjelinek } 6383e1bd7a2Ssjelinek 6393e1bd7a2Ssjelinek int 6403e1bd7a2Ssjelinek check_label_with_swap() 6413e1bd7a2Ssjelinek { 6423e1bd7a2Ssjelinek int i; 6433e1bd7a2Ssjelinek struct swaptable *st; 6443e1bd7a2Ssjelinek struct swapent *swapent; 6453e1bd7a2Ssjelinek int part; 6463e1bd7a2Ssjelinek int bm_swap = 0; 6473e1bd7a2Ssjelinek 6483e1bd7a2Ssjelinek /* 6493e1bd7a2Ssjelinek * If we are only checking part of the disk, the disk must 6503e1bd7a2Ssjelinek * have a partition map to check against. If it doesn't, 6513e1bd7a2Ssjelinek * we hope for the best. 6523e1bd7a2Ssjelinek */ 6533e1bd7a2Ssjelinek if (cur_parts == NULL) 6543e1bd7a2Ssjelinek return (0); /* Will be checked later */ 6553e1bd7a2Ssjelinek 6563e1bd7a2Ssjelinek /* 6573e1bd7a2Ssjelinek * Check for swap entries 6583e1bd7a2Ssjelinek */ 6593e1bd7a2Ssjelinek st = getswapentries(); 6603e1bd7a2Ssjelinek /* 6613e1bd7a2Ssjelinek * if there are no swap entries return. 6623e1bd7a2Ssjelinek */ 6633e1bd7a2Ssjelinek if (st == (struct swaptable *)NULL) 6643e1bd7a2Ssjelinek return (0); 6653e1bd7a2Ssjelinek swapent = st->swt_ent; 6663e1bd7a2Ssjelinek for (i = 0; i < st->swt_n; i++, swapent++) 6673e1bd7a2Ssjelinek if ((part = getpartition(swapent->ste_path)) != -1) 6683e1bd7a2Ssjelinek bm_swap |= (1 << part); 6693e1bd7a2Ssjelinek freeswapentries(st); 6703e1bd7a2Ssjelinek 6713e1bd7a2Ssjelinek return (checkpartitions(bm_swap)); 6723e1bd7a2Ssjelinek } 6733e1bd7a2Ssjelinek 6743e1bd7a2Ssjelinek /* 6753e1bd7a2Ssjelinek * Check the new label with the existing label on the disk, 6763e1bd7a2Ssjelinek * to make sure that any mounted partitions are not being 6773e1bd7a2Ssjelinek * affected by writing the new label. 6783e1bd7a2Ssjelinek */ 6793e1bd7a2Ssjelinek int 6803e1bd7a2Ssjelinek check_label_with_mount() 6813e1bd7a2Ssjelinek { 6823e1bd7a2Ssjelinek FILE *fp; 6833e1bd7a2Ssjelinek int part; 6843e1bd7a2Ssjelinek struct mnttab mnt_record; 6853e1bd7a2Ssjelinek struct mnttab *mp = &mnt_record; 6863e1bd7a2Ssjelinek int bm_mounted = 0; 6873e1bd7a2Ssjelinek 6883e1bd7a2Ssjelinek 6893e1bd7a2Ssjelinek /* 6903e1bd7a2Ssjelinek * If we are only checking part of the disk, the disk must 6913e1bd7a2Ssjelinek * have a partition map to check against. If it doesn't, 6923e1bd7a2Ssjelinek * we hope for the best. 6933e1bd7a2Ssjelinek */ 6943e1bd7a2Ssjelinek if (cur_parts == NULL) 6953e1bd7a2Ssjelinek return (0); /* Will be checked later */ 6963e1bd7a2Ssjelinek 6973e1bd7a2Ssjelinek /* 6983e1bd7a2Ssjelinek * Lock out interrupts because of the mntent protocol. 6993e1bd7a2Ssjelinek */ 7003e1bd7a2Ssjelinek enter_critical(); 7013e1bd7a2Ssjelinek /* 7023e1bd7a2Ssjelinek * Open the mount table. 7033e1bd7a2Ssjelinek */ 7043e1bd7a2Ssjelinek fp = fopen(MNTTAB, "r"); 7053e1bd7a2Ssjelinek if (fp == NULL) { 7063e1bd7a2Ssjelinek err_print("Unable to open mount table.\n"); 7073e1bd7a2Ssjelinek fullabort(); 7083e1bd7a2Ssjelinek } 7093e1bd7a2Ssjelinek /* 7103e1bd7a2Ssjelinek * Loop through the mount table until we run out of entries. 7113e1bd7a2Ssjelinek */ 7123e1bd7a2Ssjelinek while ((getmntent(fp, mp)) != -1) { 7133e1bd7a2Ssjelinek if ((part = getpartition(mp->mnt_special)) != -1) 7143e1bd7a2Ssjelinek bm_mounted |= (1 << part); 7153e1bd7a2Ssjelinek } 7163e1bd7a2Ssjelinek /* 7173e1bd7a2Ssjelinek * Close down the mount table. 7183e1bd7a2Ssjelinek */ 7193e1bd7a2Ssjelinek (void) fclose(fp); 7203e1bd7a2Ssjelinek exit_critical(); 7213e1bd7a2Ssjelinek 7223e1bd7a2Ssjelinek return (checkpartitions(bm_mounted)); 7233e1bd7a2Ssjelinek 7243e1bd7a2Ssjelinek } 7253e1bd7a2Ssjelinek 7263e1bd7a2Ssjelinek /* 7273e1bd7a2Ssjelinek * This Routine checks if any partitions specified 7283e1bd7a2Ssjelinek * are affected by writing the new label 7293e1bd7a2Ssjelinek */ 7303e1bd7a2Ssjelinek static int 7313e1bd7a2Ssjelinek checkpartitions(int bm_mounted) 7323e1bd7a2Ssjelinek { 7333e1bd7a2Ssjelinek struct dk_map32 *n; 7343e1bd7a2Ssjelinek struct dk_map *o; 7353e1bd7a2Ssjelinek struct dk_allmap old_map; 7363e1bd7a2Ssjelinek int i, found = 0; 7373e1bd7a2Ssjelinek 7383e1bd7a2Ssjelinek /* 7393e1bd7a2Ssjelinek * Now we need to check that the current partition list and the 7403e1bd7a2Ssjelinek * previous partition list (which there must be if we actually 7413e1bd7a2Ssjelinek * have partitions mounted) overlap in any way on the mounted 7423e1bd7a2Ssjelinek * partitions 7433e1bd7a2Ssjelinek */ 7443e1bd7a2Ssjelinek 7453e1bd7a2Ssjelinek /* 7463e1bd7a2Ssjelinek * Get the "real" (on-disk) version of the partition table 7473e1bd7a2Ssjelinek */ 7483e1bd7a2Ssjelinek if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) { 7493e1bd7a2Ssjelinek err_print("Unable to get current partition map.\n"); 7503e1bd7a2Ssjelinek return (-1); 7513e1bd7a2Ssjelinek } 7523e1bd7a2Ssjelinek for (i = 0; i < NDKMAP; i++) { 7533e1bd7a2Ssjelinek if (bm_mounted & (1 << i)) { 7543e1bd7a2Ssjelinek /* 7553e1bd7a2Ssjelinek * This partition is mounted 7563e1bd7a2Ssjelinek */ 7573e1bd7a2Ssjelinek o = &old_map.dka_map[i]; 7583e1bd7a2Ssjelinek n = &cur_parts->pinfo_map[i]; 7593e1bd7a2Ssjelinek #ifdef DEBUG 7603e1bd7a2Ssjelinek fmt_print( 7613e1bd7a2Ssjelinek "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE); 7623e1bd7a2Ssjelinek #endif 7633e1bd7a2Ssjelinek /* 7643e1bd7a2Ssjelinek * If partition is identical, we're fine. 7653e1bd7a2Ssjelinek * If the partition grows, we're also fine, because 7663e1bd7a2Ssjelinek * the routines in partition.c check for overflow. 7673e1bd7a2Ssjelinek * It will (ultimately) be up to the routines in 7683e1bd7a2Ssjelinek * partition.c to warn about creation of overlapping 7693e1bd7a2Ssjelinek * partitions 7703e1bd7a2Ssjelinek */ 7713e1bd7a2Ssjelinek if (o->dkl_cylno == n->dkl_cylno && 7723e1bd7a2Ssjelinek o->dkl_nblk <= n->dkl_nblk) { 7733e1bd7a2Ssjelinek #ifdef DEBUG 7743e1bd7a2Ssjelinek if (o->dkl_nblk < n->dkl_nblk) { 7753e1bd7a2Ssjelinek fmt_print( 7763e1bd7a2Ssjelinek "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk); 7773e1bd7a2Ssjelinek } 7783e1bd7a2Ssjelinek fmt_print("\n"); 7793e1bd7a2Ssjelinek #endif 7803e1bd7a2Ssjelinek continue; 7813e1bd7a2Ssjelinek } 7823e1bd7a2Ssjelinek #ifdef DEBUG 7833e1bd7a2Ssjelinek fmt_print("- changes; old (%d,%d)->new (%d,%d)\n", 7843e1bd7a2Ssjelinek o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, 7853e1bd7a2Ssjelinek n->dkl_nblk); 7863e1bd7a2Ssjelinek #endif 7873e1bd7a2Ssjelinek found = -1; 7883e1bd7a2Ssjelinek } 7893e1bd7a2Ssjelinek if (found) 7903e1bd7a2Ssjelinek break; 7913e1bd7a2Ssjelinek } 7923e1bd7a2Ssjelinek 7933e1bd7a2Ssjelinek /* 7943e1bd7a2Ssjelinek * If we found trouble and we're running from a command file, 7953e1bd7a2Ssjelinek * quit before doing something we really regret. 7963e1bd7a2Ssjelinek */ 7973e1bd7a2Ssjelinek 7983e1bd7a2Ssjelinek if (found && option_f) { 7993e1bd7a2Ssjelinek err_print("Operation on mounted disks or \ 8003e1bd7a2Ssjelinek disks currently being used for swapping must be interactive.\n"); 8013e1bd7a2Ssjelinek cmdabort(SIGINT); 8023e1bd7a2Ssjelinek } 8033e1bd7a2Ssjelinek /* 8043e1bd7a2Ssjelinek * Return the result. 8053e1bd7a2Ssjelinek */ 8063e1bd7a2Ssjelinek return (found); 8073e1bd7a2Ssjelinek } 808