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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * preenlib interface for SVM. 31 * 32 * On startup fsck attempts to check filesystems in parallel. However 33 * running mutiple fscks on the same disk at the same time 34 * significantly degrades the performance. fsck code avoids such 35 * behavior. To analyse such patterns it needs the physical disk 36 * instance. preen_build_devs provides that information for 37 * filesystems that are on top of metadevices. 38 */ 39 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 66 get_major_from_n2m(char *modname, int *major) 67 { 68 FILE *fp; 69 char drv[FILENAME_MAX + 1]; 70 int entry; 71 int found = 0; 72 char line[MAX_N2M_ALIAS_LINE]; 73 int status = 0; 74 75 if ((fp = fopen(NAME_TO_MAJOR, "r")) == NULL) { 76 return (-1); 77 } 78 79 while ((fgets(line, sizeof (line), fp) != NULL) && 80 status == 0) { 81 82 if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s %d", 83 drv, &entry) != 2) { 84 status = -1; 85 } 86 if (strcmp(drv, modname) == 0) { 87 *major = entry; 88 found = 1; 89 break; 90 } 91 } 92 93 /* 94 * if no match is found return -1 95 */ 96 if (found == 0) 97 status = -1; 98 99 (void) fclose(fp); 100 return (status); 101 } 102 103 /* 104 * If the name contains a diskset name, it is parsed out and returned. 105 * The dev_path can be either a md pathname /dev/md/rdsk/d0 or a path 106 * name that contains a diskset /dev/md/red/rdsk/d0. 107 */ 108 109 static char * 110 parse_path(char *dev_path) 111 { 112 char *cpdev; 113 char *cp, *cpp; 114 char *setname; 115 size_t size; 116 117 /* 118 * paths are /dev/md/rdsk/dx or /dev/md/<setname>/rdsk/dx 119 * cp points to /rdsk/dx. Scan back to the previous slash. 120 * If this matches "dev", then path is a local set. 121 * 122 * The /rdsk/d pattern in strstr is used so that users with 123 * a twisted mind can create a diskset called "rdsk" and 124 * would still want everything to work!! 125 */ 126 cp = strstr(dev_path, "/rdsk/d"); 127 128 for (cpdev = cp - 1; *cpdev != '/'; cpdev--); 129 cpdev = cpdev - 3; /* backspace 3 char */ 130 if (strncmp(cpdev, "dev", strlen("dev")) == 0) 131 return (Strdup(MD_LOCAL_NAME)); 132 133 /* 134 * extract the setname from the path 135 */ 136 cpp = cp; 137 for (cp--; *cp != '/'; cp--); 138 size = (size_t)(cpp - cp); 139 setname = (char *)Malloc(size); 140 (void) strlcpy(setname, (const char *)(cp + 1), size); 141 142 return (setname); 143 } 144 145 /* 146 * This routine is called from preenlib the first time. It is then 147 * recursively called through preen_subdev. 148 * 149 * The argument passed in (uname) starts with the special device from 150 * /etc/vfstab. Recursive calls pass in the underlying physical device 151 * names. 152 */ 153 void 154 preen_build_devs( 155 char *uname, /* name of metadevice */ 156 struct dk_cinfo *dkiop, /* associated controller info */ 157 void *dp /* magic info */ 158 ) 159 { 160 char *setname = NULL; 161 mdsetname_t *sp; 162 mdname_t *namep; /* metadevice name */ 163 mdnamelist_t *nlp = NULL; /* list of real devices */ 164 mdnamelist_t *p; 165 devid_nmlist_t *nm_list = NULL; 166 md_error_t status = mdnullerror; 167 md_error_t *ep = &status; 168 int ep_valid = 0; /* does ep contain a real error */ 169 struct stat statb; 170 static int md_major = -1; 171 side_t sideno; 172 173 /* 174 * The rest of the code in this library can't work within a 175 * non-global zone so we just return the top level metadevice back 176 * to be fscked. 177 */ 178 if (getzoneid() != GLOBAL_ZONEID) { 179 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 180 dkiop->dki_unit); 181 return; 182 } 183 184 if (stat(uname, &statb) != 0) 185 return; 186 187 if (md_major == -1 && 188 get_major_from_n2m(MD_MODULE, &md_major) != 0) 189 return; 190 191 /* 192 * If the path passed in is not a metadevice, then add that 193 * device to the list (preen_addunit) since it has to be a 194 * physical device. 195 */ 196 197 if (major(statb.st_rdev) != md_major) { 198 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 199 dkiop->dki_unit); 200 return; 201 } 202 /* 203 * Bind to the cluster library 204 */ 205 206 if (sdssc_bind_library() == SDSSC_ERROR) 207 return; 208 209 if (md_init_daemon("fsck", ep) != 0) { 210 ep_valid = 1; 211 goto out; 212 } 213 214 /* 215 * parse the path name to get the diskset name. 216 */ 217 218 setname = parse_path(uname); 219 if ((sp = metasetname(setname, ep)) == NULL) { 220 ep_valid = 1; 221 goto out; 222 } 223 224 /* check for ownership */ 225 if (meta_check_ownership(sp, ep) != 0) { 226 /* 227 * Don't own the set but we are here implies 228 * that this is a clustered proxy device. Simply add 229 * the unit. 230 */ 231 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 232 dkiop->dki_unit); 233 ep_valid = 1; 234 goto out; 235 } 236 237 /* 238 * get list of underlying physical devices. 239 */ 240 if ((namep = metaname(&sp, uname, ep)) == NULL) { 241 ep_valid = 1; 242 goto out; 243 } 244 245 if (namep->dev == NODEV64) { 246 goto out; 247 } 248 249 if (meta_getdevs(sp, namep, &nlp, ep) != 0) { 250 ep_valid = 1; 251 goto out; 252 } 253 254 if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) { 255 ep_valid = 1; 256 goto out; 257 } 258 259 /* gather and add the underlying devs */ 260 for (p = nlp; (p != NULL); p = p->next) { 261 mdname_t *devnp = p->namep; 262 int fd; 263 struct dk_cinfo cinfo; 264 ddi_devid_t md_did; 265 char *devname; 266 char *minor_name = NULL; 267 char mname[MAXPATHLEN]; 268 269 /* 270 * we don't want to use the rname anymore because 271 * that may have changed. Use the device id information 272 * to find the correct ctd name and open based on that. 273 * If there isn't a devid or we have a did device, then 274 * use the rname. In clustering, it's corrected for us. 275 * If no devid it's at least worth a try. 276 */ 277 if (((md_did = meta_getdidbykey(sp->setno, sideno, 278 devnp->key, ep)) == NULL) || ((minor_name = 279 meta_getdidminorbykey(sp->setno, sideno, 280 devnp->key, ep)) == NULL)) { 281 devname = devnp->rname; 282 if (md_did) 283 Free(md_did); 284 } else { 285 if (strstr(minor_name, ",raw") == NULL) { 286 (void) snprintf(mname, MAXPATHLEN, "%s,raw", 287 minor_name); 288 } else { 289 (void) snprintf(mname, MAXPATHLEN, "%s", 290 minor_name); 291 } 292 293 /* 294 * We need to make sure we call this with a specific 295 * mname (raw mname) so that we get the exact slice 296 * with the given device id. Otherwise we could try 297 * to open a slice that doesn't really exist. 298 */ 299 if (meta_deviceid_to_nmlist("/dev", md_did, 300 mname, &nm_list) != 0) { 301 (void) mdsyserror(ep, errno, devnp->rname); 302 ep_valid = 1; 303 Free(md_did); 304 Free(minor_name); 305 goto out; 306 } 307 devname = Strdup(nm_list->devname); 308 Free(md_did); 309 Free(minor_name); 310 devid_free_nmlist(nm_list); 311 } 312 /* get device name and (real) cinfo */ 313 if ((fd = open(devname, O_RDONLY, 0)) < 0) { 314 (void) mdsyserror(ep, errno, devname); 315 ep_valid = 1; 316 goto out; 317 } 318 319 if (ioctl(fd, DKIOCINFO, &cinfo) != 0) { 320 (void) mdsyserror(ep, errno, devname); 321 (void) close(fd); 322 ep_valid = 1; 323 goto out; 324 } 325 (void) close(fd); /* sd/ssd bug */ 326 327 /* 328 * preen_subdev fails when the device name has been 329 * resolved to the physical layer. Hence it is added 330 * to preen_addunit. 331 */ 332 if (preen_subdev(devname, &cinfo, dp) != 0) { 333 preen_addunit(dp, cinfo.dki_dname, NULL, NULL, 334 cinfo.dki_unit); 335 } 336 } 337 338 /* cleanup, if we fail, just add this composite device to the list */ 339 out: 340 if (setname != NULL) 341 Free(setname); 342 if (ep_valid != 0) { 343 mde_perror(&status, ""); 344 mdclrerror(&status); 345 } 346 metafreenamelist(nlp); 347 } 348