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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * preenlib interface for SVM. 30 * 31 * On startup fsck attempts to check filesystems in parallel. However 32 * running mutiple fscks on the same disk at the same time 33 * significantly degrades the performance. fsck code avoids such 34 * behavior. To analyse such patterns it needs the physical disk 35 * instance. preen_build_devs provides that information for 36 * filesystems that are on top of metadevices. 37 */ 38 39 #include <ctype.h> 40 #include <meta.h> 41 #include <limits.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <zone.h> 45 46 #include <sdssc.h> 47 48 #define MAX_N2M_ALIAS_LINE (2*FILENAME_MAX + 1) 49 #define NAME_TO_MAJOR "/etc/name_to_major" 50 #define MD_MODULE "md" 51 52 /* 53 * Macros to produce a quoted string containing the value of a 54 * preprocessor macro. For example, if SIZE is defined to be 256, 55 * VAL2STR(SIZE) is "256". This is used to construct format 56 * strings for scanf-family functions below. 57 */ 58 #define QUOTE(x) #x 59 #define VAL2STR(x) QUOTE(x) 60 61 extern void preen_addunit(void *cookie, char *dname, int (*cf)(), 62 void *datap, uint_t unit); 63 extern int preen_subdev(char *name, struct dk_cinfo *dkiop, void *dp); 64 65 static int is_blank(char *); 66 67 /* 68 * is_blank() returns 1 (true) if a line specified is composed of 69 * whitespace characters only. otherwise, it returns 0 (false). 70 * 71 * Note. the argument (line) must be null-terminated. 72 */ 73 static int 74 is_blank(char *line) 75 { 76 for (/* nothing */; *line != '\0'; line++) 77 if (!isspace(*line)) 78 return (0); 79 return (1); 80 } 81 82 static int 83 get_major_from_n2m(char *modname, int *major) 84 { 85 FILE *fp; 86 char drv[FILENAME_MAX + 1]; 87 int entry; 88 int found = 0; 89 char line[MAX_N2M_ALIAS_LINE], *cp; 90 int status = 0; 91 92 if ((fp = fopen(NAME_TO_MAJOR, "r")) == NULL) { 93 return (-1); 94 } 95 96 while ((fgets(line, sizeof (line), fp) != NULL) && 97 status == 0) { 98 /* cut off comments starting with '#' */ 99 if ((cp = strchr(line, '#')) != NULL) 100 *cp = '\0'; 101 /* ignore comment or blank lines */ 102 if (is_blank(line)) 103 continue; 104 /* sanity-check */ 105 if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s %d", 106 drv, &entry) != 2) { 107 status = -1; 108 } 109 if (strcmp(drv, modname) == 0) { 110 *major = entry; 111 found = 1; 112 break; 113 } 114 } 115 116 /* 117 * if no match is found return -1 118 */ 119 if (found == 0) 120 status = -1; 121 122 (void) fclose(fp); 123 return (status); 124 } 125 126 /* 127 * This routine is called from preenlib the first time. It is then 128 * recursively called through preen_subdev. 129 * 130 * The argument passed in (uname) starts with the special device from 131 * /etc/vfstab. Recursive calls pass in the underlying physical device 132 * names. 133 */ 134 void 135 preen_build_devs( 136 char *uname, /* name of metadevice */ 137 struct dk_cinfo *dkiop, /* associated controller info */ 138 void *dp /* magic info */ 139 ) 140 { 141 char *setname = NULL; 142 char *tname = NULL; 143 mdsetname_t *sp; 144 mdname_t *namep; /* metadevice name */ 145 mdnamelist_t *nlp = NULL; /* list of real devices */ 146 mdnamelist_t *p; 147 devid_nmlist_t *nm_list = NULL; 148 md_error_t status = mdnullerror; 149 md_error_t *ep = &status; 150 int ep_valid = 0; /* does ep contain a real error */ 151 struct stat statb; 152 static int md_major = -1; 153 side_t sideno; 154 155 /* 156 * The rest of the code in this library can't work within a 157 * non-global zone so we just return the top level metadevice back 158 * to be fscked. 159 */ 160 if (getzoneid() != GLOBAL_ZONEID) { 161 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 162 dkiop->dki_unit); 163 return; 164 } 165 166 if (stat(uname, &statb) != 0) 167 return; 168 169 if (md_major == -1 && 170 get_major_from_n2m(MD_MODULE, &md_major) != 0) 171 return; 172 173 /* 174 * If the path passed in is not a metadevice, then add that 175 * device to the list (preen_addunit) since it has to be a 176 * physical device. 177 */ 178 179 if (major(statb.st_rdev) != md_major) { 180 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 181 dkiop->dki_unit); 182 return; 183 } 184 /* 185 * Bind to the cluster library 186 */ 187 188 if (sdssc_bind_library() == SDSSC_ERROR) 189 return; 190 191 if (md_init_daemon("fsck", ep) != 0) { 192 ep_valid = 1; 193 goto out; 194 } 195 196 /* 197 * parse the path name to get the diskset name. 198 */ 199 parse_device(NULL, uname, &tname, &setname); 200 Free(tname); 201 if ((sp = metasetname(setname, ep)) == NULL) { 202 ep_valid = 1; 203 goto out; 204 } 205 206 /* check for ownership */ 207 if (meta_check_ownership(sp, ep) != 0) { 208 /* 209 * Don't own the set but we are here implies 210 * that this is a clustered proxy device. Simply add 211 * the unit. 212 */ 213 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 214 dkiop->dki_unit); 215 ep_valid = 1; 216 goto out; 217 } 218 219 /* 220 * get list of underlying physical devices. 221 */ 222 if ((namep = metaname(&sp, uname, UNKNOWN, ep)) == NULL) { 223 ep_valid = 1; 224 goto out; 225 } 226 227 if (namep->dev == NODEV64) { 228 goto out; 229 } 230 231 if (meta_getdevs(sp, namep, &nlp, ep) != 0) { 232 ep_valid = 1; 233 goto out; 234 } 235 236 if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) { 237 ep_valid = 1; 238 goto out; 239 } 240 241 /* gather and add the underlying devs */ 242 for (p = nlp; (p != NULL); p = p->next) { 243 mdname_t *devnp = p->namep; 244 int fd; 245 struct dk_cinfo cinfo; 246 ddi_devid_t md_did; 247 char *devname; 248 char *minor_name = NULL; 249 char mname[MAXPATHLEN]; 250 251 /* 252 * we don't want to use the rname anymore because 253 * that may have changed. Use the device id information 254 * to find the correct ctd name and open based on that. 255 * If there isn't a devid or we have a did device, then 256 * use the rname. In clustering, it's corrected for us. 257 * If no devid it's at least worth a try. 258 */ 259 if (((md_did = meta_getdidbykey(sp->setno, sideno, 260 devnp->key, ep)) == NULL) || ((minor_name = 261 meta_getdidminorbykey(sp->setno, sideno, 262 devnp->key, ep)) == NULL)) { 263 devname = devnp->rname; 264 if (md_did) 265 Free(md_did); 266 } else { 267 if (strstr(minor_name, ",raw") == NULL) { 268 (void) snprintf(mname, MAXPATHLEN, "%s,raw", 269 minor_name); 270 } else { 271 (void) snprintf(mname, MAXPATHLEN, "%s", 272 minor_name); 273 } 274 275 /* 276 * We need to make sure we call this with a specific 277 * mname (raw mname) so that we get the exact slice 278 * with the given device id. Otherwise we could try 279 * to open a slice that doesn't really exist. 280 */ 281 if (meta_deviceid_to_nmlist("/dev", md_did, 282 mname, &nm_list) != 0) { 283 (void) mdsyserror(ep, errno, devnp->rname); 284 ep_valid = 1; 285 Free(md_did); 286 Free(minor_name); 287 goto out; 288 } 289 devname = Strdup(nm_list->devname); 290 Free(md_did); 291 Free(minor_name); 292 devid_free_nmlist(nm_list); 293 } 294 /* get device name and (real) cinfo */ 295 if ((fd = open(devname, O_RDONLY, 0)) < 0) { 296 (void) mdsyserror(ep, errno, devname); 297 ep_valid = 1; 298 /* 299 * We need to scan all sub devices even if some fail 300 * since exit here would end up in not finishing fsck 301 * on all devices and prevent us from going into 302 * multiuser mode. 303 */ 304 continue; 305 } 306 307 if (ioctl(fd, DKIOCINFO, &cinfo) != 0) { 308 (void) mdsyserror(ep, errno, devname); 309 (void) close(fd); 310 ep_valid = 1; 311 /* Continue here too. See comment from before */ 312 continue; 313 } 314 (void) close(fd); /* sd/ssd bug */ 315 316 /* 317 * preen_subdev fails when the device name has been 318 * resolved to the physical layer. Hence it is added 319 * to preen_addunit. 320 */ 321 if (preen_subdev(devname, &cinfo, dp) != 0) { 322 preen_addunit(dp, cinfo.dki_dname, NULL, NULL, 323 cinfo.dki_unit); 324 } 325 } 326 327 /* cleanup, if we fail, just add this composite device to the list */ 328 out: 329 if (setname != NULL) 330 Free(setname); 331 if (ep_valid != 0) { 332 mde_perror(&status, ""); 333 mdclrerror(&status); 334 } 335 metafreenamelist(nlp); 336 } 337