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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "@(#)findap.c 1.18 09/05/26 SMI" 28 29 #include <unistd.h> 30 #include <ftw.h> 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <strings.h> 34 #include <search.h> 35 #include <errno.h> 36 #include <sys/vtoc.h> 37 38 #define COMMA ',' 39 #define COLON ':' 40 41 #include "findap.h" 42 struct devstore { 43 struct other_paths *raw_or_cooked[2]; 44 }; 45 struct partstore { 46 int nparts; 47 struct devstore *devs; 48 }; 49 struct lunstore { 50 int nluns; 51 struct partstore *parts; 52 }; 53 struct pathstore { 54 char *wwa; 55 struct lunstore luns; 56 }; 57 58 static struct pathstore *ps; 59 static int last_pathstore; 60 #ifdef FIND_AP_STATS 61 static int hit_count; 62 static int miss_count; 63 #endif 64 static size_t ndevtree; 65 66 static int 67 pscmp(const void *a, const void *b) 68 { 69 struct pathstore *as, *bs; 70 as = (struct pathstore *)a; 71 bs = (struct pathstore *)b; 72 return (strcmp(as->wwa, bs->wwa)); 73 } 74 static int 75 add_to_dev_tree(const char *path, struct about_dev *entry) 76 { 77 struct pathstore *new_ps; 78 struct other_paths *op; 79 struct pathstore *x; 80 81 if ((new_ps = realloc(ps, (ndevtree + 1) * 82 sizeof (struct pathstore))) == NULL || 83 (op = malloc(sizeof (struct other_paths))) == NULL) { 84 return (1); 85 } 86 87 if ((x = (struct pathstore *) 88 calloc(1, sizeof (struct pathstore))) == NULL) { 89 free(op); 90 return (1); 91 } 92 op->path = strdup(path); 93 x->wwa = entry->wwa; 94 /* 95 * This extra check is a cheat, but a cheat that works. 96 * Since the devices tend to get created all at the same time 97 * nftw will tend to do all the nodes for the same disk 98 * one after another. This check stops us having to do 99 * any more lsearch() calls than are really needed. 100 */ 101 if (ps != NULL && pscmp(x, &new_ps[last_pathstore]) == 0) { 102 #ifdef FIND_AP_STATS 103 hit_count++; 104 #endif 105 ps = new_ps; 106 new_ps = &new_ps[last_pathstore]; 107 } else { 108 #ifdef FIND_AP_STATS 109 miss_count++; 110 #endif 111 ps = new_ps; 112 new_ps = lsearch(x, ps, &ndevtree, 113 sizeof (struct pathstore), pscmp); 114 last_pathstore = new_ps - ps; 115 } 116 if (new_ps->wwa != entry->wwa) 117 free(entry->wwa); 118 free(x); 119 if (new_ps->luns.nluns <= entry->lun) { 120 struct partstore *tmp; 121 int i; 122 123 tmp = realloc(new_ps->luns.parts, sizeof (struct partstore) * 124 (entry->lun + 1)); 125 if (tmp == NULL) 126 return (1); 127 for (i = new_ps->luns.nluns; i <= entry->lun; i++) { 128 tmp[i].nparts = 0; 129 tmp[i].devs = NULL; 130 } 131 new_ps->luns.parts = tmp; 132 new_ps->luns.nluns = entry->lun + 1; 133 } 134 if (new_ps->luns.parts[entry->lun].nparts <= entry->part) { 135 struct devstore *tmp; 136 int i, nparts; 137 138 nparts = entry->part + 1; 139 if (nparts < V_NUMPAR) 140 nparts = V_NUMPAR; 141 142 tmp = realloc(new_ps->luns.parts[entry->lun].devs, 143 sizeof (struct devstore) * nparts); 144 if (tmp == NULL) 145 return (1); 146 for (i = new_ps->luns.parts[entry->lun].nparts; 147 i < nparts; i++) { 148 tmp[i].raw_or_cooked[0] = NULL; 149 tmp[i].raw_or_cooked[1] = NULL; 150 } 151 new_ps->luns.parts[entry->lun].devs = tmp; 152 new_ps->luns.parts[entry->lun].nparts = nparts; 153 } 154 155 op->next = new_ps->luns.parts[ 156 entry->lun].devs[entry->part].raw_or_cooked[entry->israw]; 157 new_ps->luns.parts[entry->lun].devs[ 158 entry->part].raw_or_cooked[entry->israw] = op; 159 160 return (0); 161 } 162 static char * 163 get_wwa(const char *path) 164 { 165 char *tofree, *tmp; 166 char *basename; 167 168 tofree = strdup(path); 169 tmp = tofree; 170 171 if (tmp == NULL) { 172 perror("strdup failed"); 173 return (NULL); 174 } 175 176 basename = strrchr(tofree, '/'); 177 178 if (basename == NULL) 179 basename = tofree; 180 else 181 basename += 1; 182 183 if (strncmp(basename, "ssd@", 4) != 0 || strlen(basename) <= 8) { 184 free(tofree); 185 return (NULL); 186 } 187 188 basename += 7; 189 190 tmp = strchr(basename, COLON); 191 if (tmp != NULL) { 192 *tmp = NULL; 193 } 194 tmp = strchr(basename, COMMA); 195 196 if (tmp != NULL) { 197 *tmp = NULL; 198 tmp = strdup(basename); 199 } else { 200 tmp = NULL; 201 } 202 203 free(tofree); 204 205 return (tmp); 206 } 207 208 static long 209 get_lun(const char *path) 210 { 211 int lun; 212 char *tmp; 213 tmp = strrchr(path, COLON); 214 215 if (tmp == NULL) 216 return (-1); 217 while (tmp > path) { 218 if (*tmp == COMMA) 219 break; 220 tmp--; 221 } 222 if (tmp <= path) 223 return (-1); 224 tmp++; 225 226 errno = 0; 227 lun = strtol(tmp, NULL, 16); 228 229 if (lun == 0 && errno == EINVAL) { 230 return (-1); 231 } 232 return (lun); 233 } 234 static char 235 find_part(const char *path) 236 { 237 char part; 238 char *tmp; 239 240 tmp = strrchr(path, COLON); 241 242 if (tmp == NULL) 243 return (-1); 244 245 tmp++; 246 247 part = *tmp - 'a'; 248 249 if (part > (V_NUMPAR - 1) || part < 0) 250 part = -1; 251 return (part); 252 } 253 static char 254 is_raw(const char *path) 255 { 256 char *tmp; 257 258 tmp = strrchr(path, COMMA); 259 260 if (tmp == NULL) { 261 return (-1); 262 } 263 264 tmp++; 265 266 if (strcmp(tmp, "raw") == 0) 267 return (1); 268 else 269 return (0); 270 } 271 272 #ifdef NOT_USED 273 static void 274 print_dev(struct about_dev *dv) 275 { 276 char *tmp; 277 278 switch (dv->israw) { 279 case 0: tmp = "block device"; 280 break; 281 case 1: tmp = "raw device"; 282 break; 283 default: 284 tmp = "error"; 285 break; 286 } 287 (void) printf("WWA = %s, part = %c, %s\n", 288 dv->wwa == NULL ? "(nil)" : dv->wwa, 289 dv->part + 'a', tmp); 290 } 291 #endif 292 static void 293 print_otherpaths(struct other_paths *paths) 294 { 295 while (paths != NULL) { 296 (void) printf("%s\n", paths->path); 297 paths = paths->next; 298 } 299 } 300 void 301 print_paths(struct paths *paths) 302 { 303 (void) printf("Logical path %s\n", paths->logicalpath); 304 (void) print_otherpaths(paths->op); 305 } 306 307 static void 308 free_otherpaths(struct other_paths *paths) 309 { 310 struct other_paths *p; 311 312 while (paths) { 313 p = paths->next; 314 free(paths->path); 315 free(paths); 316 paths = p; 317 } 318 } 319 void 320 free_paths(struct paths *paths) 321 { 322 free_otherpaths(paths->op); 323 if (paths->logicalpath != NULL) 324 free(paths->logicalpath); 325 free(paths); 326 } 327 static struct paths * 328 dup_paths(struct paths *src) 329 { 330 struct paths *top; 331 struct other_paths *tmp, *srctmp; 332 333 if (src == NULL || (top = calloc(1, sizeof (struct paths))) == NULL) { 334 return (NULL); 335 } 336 if ((top->logicalpath = strdup(src->logicalpath)) == NULL) { 337 free(top); 338 return (NULL); 339 } 340 if ((top->op = calloc(1, sizeof (struct other_paths))) == NULL) { 341 free_paths(top); 342 return (NULL); 343 } 344 for (tmp = top->op, srctmp = src->op; srctmp != NULL; 345 srctmp = srctmp->next) { 346 if ((tmp->path = strdup(srctmp->path)) == NULL) { 347 free_paths(top); 348 return (NULL); 349 } 350 if (srctmp->next == NULL) 351 break; 352 if ((tmp->next = calloc(1, sizeof (struct other_paths))) == 353 NULL) { 354 free(tmp->path); 355 free_paths(top); 356 return (NULL); 357 } 358 tmp = tmp->next; 359 } 360 return (top); 361 } 362 /*ARGSUSED*/ 363 static int 364 ftwfunc(const char *path, const struct stat *stat, int flag, struct FTW *ftw) 365 { 366 struct about_dev thisdv; 367 if (flag != FTW_F) 368 return (0); 369 370 371 thisdv.wwa = get_wwa(path); 372 thisdv.part = find_part(path); 373 thisdv.lun = get_lun(path); 374 thisdv.israw = is_raw(path); 375 376 if (thisdv.lun != -1 && thisdv.part != -1 && thisdv.wwa != NULL) { 377 return (add_to_dev_tree(path, &thisdv)); 378 } 379 return (0); 380 } 381 char * 382 full_path(char *link, char *target) 383 { 384 char *tmp_target, *tmp_link; 385 if ((link = strdup(link)) == NULL) 386 return (NULL); 387 /* first remove the link from the path leaving just the directories */ 388 tmp_link = strrchr(link, '/'); 389 if (tmp_link != NULL) { 390 *tmp_link = NULL; 391 } 392 393 for (tmp_target = target; strncmp("../", tmp_target, 3) == 0; 394 tmp_target += 3) { 395 tmp_link = strrchr(link, '/'); 396 if (tmp_link != NULL) { 397 *tmp_link = NULL; 398 } 399 } 400 tmp_link = malloc(strlen(link) + 1 + strlen(tmp_target) + 1); 401 if (tmp_link != NULL) 402 (void) sprintf(tmp_link, "%s/%s", link, tmp_target); 403 404 free(link); 405 return (tmp_link); 406 } 407 struct paths * 408 findap(const char *inpath, const char *devices) 409 { 410 char link_data[1024]; 411 char *path, *fullpath = NULL; 412 struct paths *pa; 413 struct pathstore *entry; 414 struct about_dev dv; 415 struct pathstore this_ps; 416 417 path = strdup(inpath); 418 419 if (path == NULL) 420 return (NULL); 421 422 (void) memset(&link_data[0], NULL, 1024); 423 424 if (readlink(path, &link_data[0], 1024) == -1) { 425 426 dv.part = find_part(path); 427 if (dv.part == -1) { 428 free(path); 429 return (NULL); 430 } 431 dv.wwa = get_wwa(path); 432 dv.lun = get_lun(path); 433 dv.israw = is_raw(path); 434 fullpath = strdup(path); 435 } else { 436 dv.wwa = get_wwa(&link_data[0]); 437 dv.lun = get_lun(&link_data[0]); 438 dv.part = find_part(&link_data[0]); 439 if (dv.part == -1) { 440 free(path); 441 return (NULL); 442 } 443 dv.israw = is_raw(&link_data[0]); 444 fullpath = full_path(path, &link_data[0]); 445 } 446 if (fullpath == NULL) { 447 free(path); 448 if (dv.wwa != NULL) { 449 free(dv.wwa); 450 } 451 return (NULL); 452 } 453 if (dv.wwa == NULL) { 454 pa = (struct paths *)calloc(1, sizeof (struct paths)); 455 pa->logicalpath = path; 456 if ((pa->op = calloc(1, sizeof (struct other_paths))) == NULL) { 457 free(pa); 458 pa = NULL; 459 free(fullpath); 460 } else { 461 pa->op->path = fullpath; 462 } 463 return (pa); 464 } 465 this_ps.wwa = dv.wwa; 466 467 if (ndevtree == 0) { 468 (void) nftw(devices, ftwfunc, 64, FTW_MOUNT); 469 qsort(ps, ndevtree, sizeof (struct pathstore), pscmp); 470 } 471 472 entry = bsearch(&this_ps, (void *)ps, ndevtree, 473 sizeof (struct pathstore), pscmp); 474 free(dv.wwa); 475 if (entry != NULL) { 476 struct other_paths **ep, *prev = NULL; 477 struct paths *pa_out; 478 479 if (*(ep = &entry->luns.parts[dv.lun].devs[ 480 dv.part].raw_or_cooked[dv.israw]) == NULL) 481 return (NULL); 482 483 pa = (struct paths *)calloc(1, sizeof (struct paths)); 484 if (pa != NULL) { 485 for (pa->op = *ep; pa->op != NULL && 486 strcmp(pa->op->path, fullpath); 487 pa->op = pa->op->next) { 488 prev = pa->op; 489 } 490 pa->logicalpath = path; 491 if (prev != NULL && pa->op != NULL) { 492 prev->next = pa->op->next; 493 pa->op->next = *ep; 494 *ep = pa->op; 495 } 496 } else { 497 free(path); 498 } 499 if (pa->op) { 500 pa_out = dup_paths(pa); 501 } else { 502 pa_out = NULL; 503 } 504 free(pa->logicalpath); 505 free(pa); 506 free(fullpath); 507 return (pa_out); 508 } 509 free(fullpath); 510 free(path); 511 return (NULL); 512 } 513 static void 514 free_devstore(struct devstore *dev) 515 { 516 if (dev == NULL) 517 return; 518 if (dev->raw_or_cooked[0]) 519 free_otherpaths(dev->raw_or_cooked[0]); 520 if (dev->raw_or_cooked[1]) 521 free_otherpaths(dev->raw_or_cooked[1]); 522 } 523 static void 524 free_partstore(struct partstore *pas) 525 { 526 int i; 527 528 if (pas == NULL) 529 return; 530 531 for (i = 0; i < pas->nparts; i++) 532 free_devstore(&pas->devs[i]); 533 if (pas->devs) 534 free(pas->devs); 535 } 536 static void 537 free_lunstore(struct lunstore *ls) 538 { 539 int i; 540 541 if (ls == NULL) 542 return; 543 for (i = 0; i < ls->nluns; i++) 544 free_partstore(&ls->parts[i]); 545 if (ls->parts) 546 free(ls->parts); 547 } 548 void 549 findap_fini(void) 550 { 551 int i; 552 553 for (i = 0; i < ndevtree; i++) { 554 free_lunstore(&ps[i].luns); 555 free(ps[i].wwa); 556 } 557 if (ps != NULL) { 558 free(ps); 559 ps = NULL; 560 } 561 #ifdef FIND_AP_STATS 562 printf("Hits = %d\nMisses %d\n", hit_count, miss_count); 563 #endif 564 ndevtree = 0; 565 } 566