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 * If the name contains a diskset name, it is parsed out and returned. 128 * The dev_path can be either a md pathname /dev/md/rdsk/d0 or a path 129 * name that contains a diskset /dev/md/red/rdsk/d0. 130 */ 131 132 static char * 133 parse_path(char *dev_path) 134 { 135 char *cpdev; 136 char *cp, *cpp; 137 char *setname; 138 size_t size; 139 140 /* 141 * paths are /dev/md/rdsk/dx or /dev/md/<setname>/rdsk/dx 142 * cp points to /rdsk/dx. Scan back to the previous slash. 143 * If this matches "dev", then path is a local set. 144 * 145 * The /rdsk/d pattern in strstr is used so that users with 146 * a twisted mind can create a diskset called "rdsk" and 147 * would still want everything to work!! 148 */ 149 cp = strstr(dev_path, "/rdsk/d"); 150 151 for (cpdev = cp - 1; *cpdev != '/'; cpdev--); 152 cpdev = cpdev - 3; /* backspace 3 char */ 153 if (strncmp(cpdev, "dev", strlen("dev")) == 0) 154 return (Strdup(MD_LOCAL_NAME)); 155 156 /* 157 * extract the setname from the path 158 */ 159 cpp = cp; 160 for (cp--; *cp != '/'; cp--); 161 size = (size_t)(cpp - cp); 162 setname = (char *)Malloc(size); 163 (void) strlcpy(setname, (const char *)(cp + 1), size); 164 165 return (setname); 166 } 167 168 /* 169 * This routine is called from preenlib the first time. It is then 170 * recursively called through preen_subdev. 171 * 172 * The argument passed in (uname) starts with the special device from 173 * /etc/vfstab. Recursive calls pass in the underlying physical device 174 * names. 175 */ 176 void 177 preen_build_devs( 178 char *uname, /* name of metadevice */ 179 struct dk_cinfo *dkiop, /* associated controller info */ 180 void *dp /* magic info */ 181 ) 182 { 183 char *setname = NULL; 184 mdsetname_t *sp; 185 mdname_t *namep; /* metadevice name */ 186 mdnamelist_t *nlp = NULL; /* list of real devices */ 187 mdnamelist_t *p; 188 devid_nmlist_t *nm_list = NULL; 189 md_error_t status = mdnullerror; 190 md_error_t *ep = &status; 191 int ep_valid = 0; /* does ep contain a real error */ 192 struct stat statb; 193 static int md_major = -1; 194 side_t sideno; 195 196 /* 197 * The rest of the code in this library can't work within a 198 * non-global zone so we just return the top level metadevice back 199 * to be fscked. 200 */ 201 if (getzoneid() != GLOBAL_ZONEID) { 202 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 203 dkiop->dki_unit); 204 return; 205 } 206 207 if (stat(uname, &statb) != 0) 208 return; 209 210 if (md_major == -1 && 211 get_major_from_n2m(MD_MODULE, &md_major) != 0) 212 return; 213 214 /* 215 * If the path passed in is not a metadevice, then add that 216 * device to the list (preen_addunit) since it has to be a 217 * physical device. 218 */ 219 220 if (major(statb.st_rdev) != md_major) { 221 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 222 dkiop->dki_unit); 223 return; 224 } 225 /* 226 * Bind to the cluster library 227 */ 228 229 if (sdssc_bind_library() == SDSSC_ERROR) 230 return; 231 232 if (md_init_daemon("fsck", ep) != 0) { 233 ep_valid = 1; 234 goto out; 235 } 236 237 /* 238 * parse the path name to get the diskset name. 239 */ 240 241 setname = parse_path(uname); 242 if ((sp = metasetname(setname, ep)) == NULL) { 243 ep_valid = 1; 244 goto out; 245 } 246 247 /* check for ownership */ 248 if (meta_check_ownership(sp, ep) != 0) { 249 /* 250 * Don't own the set but we are here implies 251 * that this is a clustered proxy device. Simply add 252 * the unit. 253 */ 254 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 255 dkiop->dki_unit); 256 ep_valid = 1; 257 goto out; 258 } 259 260 /* 261 * get list of underlying physical devices. 262 */ 263 if ((namep = metaname(&sp, uname, UNKNOWN, ep)) == NULL) { 264 ep_valid = 1; 265 goto out; 266 } 267 268 if (namep->dev == NODEV64) { 269 goto out; 270 } 271 272 if (meta_getdevs(sp, namep, &nlp, ep) != 0) { 273 ep_valid = 1; 274 goto out; 275 } 276 277 if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) { 278 ep_valid = 1; 279 goto out; 280 } 281 282 /* gather and add the underlying devs */ 283 for (p = nlp; (p != NULL); p = p->next) { 284 mdname_t *devnp = p->namep; 285 int fd; 286 struct dk_cinfo cinfo; 287 ddi_devid_t md_did; 288 char *devname; 289 char *minor_name = NULL; 290 char mname[MAXPATHLEN]; 291 292 /* 293 * we don't want to use the rname anymore because 294 * that may have changed. Use the device id information 295 * to find the correct ctd name and open based on that. 296 * If there isn't a devid or we have a did device, then 297 * use the rname. In clustering, it's corrected for us. 298 * If no devid it's at least worth a try. 299 */ 300 if (((md_did = meta_getdidbykey(sp->setno, sideno, 301 devnp->key, ep)) == NULL) || ((minor_name = 302 meta_getdidminorbykey(sp->setno, sideno, 303 devnp->key, ep)) == NULL)) { 304 devname = devnp->rname; 305 if (md_did) 306 Free(md_did); 307 } else { 308 if (strstr(minor_name, ",raw") == NULL) { 309 (void) snprintf(mname, MAXPATHLEN, "%s,raw", 310 minor_name); 311 } else { 312 (void) snprintf(mname, MAXPATHLEN, "%s", 313 minor_name); 314 } 315 316 /* 317 * We need to make sure we call this with a specific 318 * mname (raw mname) so that we get the exact slice 319 * with the given device id. Otherwise we could try 320 * to open a slice that doesn't really exist. 321 */ 322 if (meta_deviceid_to_nmlist("/dev", md_did, 323 mname, &nm_list) != 0) { 324 (void) mdsyserror(ep, errno, devnp->rname); 325 ep_valid = 1; 326 Free(md_did); 327 Free(minor_name); 328 goto out; 329 } 330 devname = Strdup(nm_list->devname); 331 Free(md_did); 332 Free(minor_name); 333 devid_free_nmlist(nm_list); 334 } 335 /* get device name and (real) cinfo */ 336 if ((fd = open(devname, O_RDONLY, 0)) < 0) { 337 (void) mdsyserror(ep, errno, devname); 338 ep_valid = 1; 339 /* 340 * We need to scan all sub devices even if some fail 341 * since exit here would end up in not finishing fsck 342 * on all devices and prevent us from going into 343 * multiuser mode. 344 */ 345 continue; 346 } 347 348 if (ioctl(fd, DKIOCINFO, &cinfo) != 0) { 349 (void) mdsyserror(ep, errno, devname); 350 (void) close(fd); 351 ep_valid = 1; 352 /* Continue here too. See comment from before */ 353 continue; 354 } 355 (void) close(fd); /* sd/ssd bug */ 356 357 /* 358 * preen_subdev fails when the device name has been 359 * resolved to the physical layer. Hence it is added 360 * to preen_addunit. 361 */ 362 if (preen_subdev(devname, &cinfo, dp) != 0) { 363 preen_addunit(dp, cinfo.dki_dname, NULL, NULL, 364 cinfo.dki_unit); 365 } 366 } 367 368 /* cleanup, if we fail, just add this composite device to the list */ 369 out: 370 if (setname != NULL) 371 Free(setname); 372 if (ep_valid != 0) { 373 mde_perror(&status, ""); 374 mdclrerror(&status); 375 } 376 metafreenamelist(nlp); 377 } 378