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 * routines in this module are meant to be called by other libvolmgt 30 * routines only 31 */ 32 33 #include <stdio.h> 34 #include <string.h> 35 #include <dirent.h> 36 #include <fcntl.h> 37 #include <string.h> 38 #ifdef DEBUG 39 #include <errno.h> 40 #endif 41 #include <libintl.h> 42 #include <limits.h> 43 #include <unistd.h> 44 #include <stdlib.h> 45 #include <volmgt.h> 46 #include <sys/types.h> 47 #include <sys/mkdev.h> 48 #include <sys/stat.h> 49 #include <sys/dkio.h> 50 #include <sys/param.h> 51 #include <sys/wait.h> 52 #include <sys/mnttab.h> 53 #include "volmgt_private.h" 54 55 56 #define NULL_PATH "/dev/null" 57 58 59 60 /* 61 * This is an ON Consolidation Private interface. 62 * 63 * Is the specified path mounted? 64 * 65 * This function is really inadequate for ejection testing. For example, 66 * I could have /dev/fd0a mounted and eject /dev/fd0c, and it would be 67 * ejected. There needs to be some better way to make this check, although 68 * short of looking up the mounted dev_t in the kernel mount table and 69 * building in all kinds of knowledge into this function, I'm not sure 70 * how to do it. 71 */ 72 int 73 _dev_mounted(char *path) 74 { 75 static int vol_getmntdev(FILE *, struct mnttab *, dev_t, 76 struct dk_cinfo *); 77 int fd = -1; 78 struct dk_cinfo info; 79 static FILE *fp = NULL; /* mnttab file pointer */ 80 struct mnttab mnt; /* set bug not used */ 81 char *cn = NULL; /* char spcl pathname */ 82 struct stat64 sb; 83 int ret_val = 0; 84 85 86 /* ensure we have the block spcl pathname */ 87 if ((cn = (char *)volmgt_getfullrawname(path)) == NULL) { 88 goto dun; 89 } 90 91 if ((fp = fopen(MNTTAB, "rF")) == NULL) { 92 /* mtab is gone... let him go */ 93 goto dun; 94 } 95 96 if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) { 97 goto dun; 98 } 99 100 if (fstat64(fd, &sb) < 0) { 101 goto dun; 102 } 103 104 if (ioctl(fd, DKIOCINFO, &info) != 0) { 105 goto dun; 106 } 107 108 if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) { 109 ret_val = 1; /* match found! */ 110 } 111 112 dun: 113 if (cn != NULL) { 114 free(cn); 115 } 116 if (fp != NULL) { 117 (void) fclose(fp); 118 } 119 if (fd >= 0) { 120 (void) close(fd); 121 } 122 return (ret_val); 123 } 124 125 126 /* 127 * This is an ON Consolidation Private interface. 128 * 129 * Forks off rmmount and (in essence) returns the result 130 * 131 * a return value of 0 (FALSE) means failure, non-zero (TRUE) means success 132 */ 133 int 134 _dev_unmount(char *path) 135 { 136 static int call_unmount_prog(int, int, char *, int, char *, 137 char *); 138 static int get_media_info(char *, char **, int *, char **); 139 char *bn = NULL; /* block name */ 140 char *mtype = NULL; /* media type */ 141 char *spcl = NULL; /* special dev. path */ 142 char *spcl_failed = NULL; /* spcl that failed */ 143 int ret_val = FALSE; /* what we return */ 144 char *vr; /* volmgt root dir */ 145 int media_info_gotten = 0; 146 int mnum = 0; 147 int volume_is_not_managed; 148 char *pathbuf, *absname; 149 150 151 if ((bn = (char *)volmgt_getfullblkname(path)) == NULL) { 152 goto dun; 153 } 154 155 if ((pathbuf = malloc(PATH_MAX+1)) == NULL) 156 goto dun; 157 158 absname = bn; 159 if (realpath(bn, pathbuf) != NULL) 160 absname = pathbuf; 161 162 volume_is_not_managed = !volmgt_running() || 163 (!volmgt_ownspath(absname) && volmgt_symname(bn) == NULL); 164 165 free(pathbuf); 166 167 /* decide of we should use rmmount to unmount the media */ 168 if (!volume_is_not_managed) { 169 int use_rmm = FALSE; /* use rmmount?? */ 170 171 /* at least volmgt is running */ 172 vr = (char *)volmgt_root(); 173 if (strncmp(bn, vr, strlen(vr)) == 0) { 174 /* the block path is rooted in /vol */ 175 use_rmm = TRUE; 176 } 177 178 /* try to get info about media */ 179 media_info_gotten = get_media_info(bn, &mtype, &mnum, &spcl); 180 181 ret_val = call_unmount_prog(media_info_gotten, use_rmm, mtype, 182 mnum, spcl, bn); 183 184 } else { 185 186 /* volmgt is *not* running */ 187 188 if (get_media_info(bn, &mtype, &mnum, &spcl)) { 189 190 /* 191 * volmgt is off and get_media_info() has returned 192 * info on the media -- soo (this is kinda' a hack) 193 * ... we iterate, looking for multiple slices 194 * of (say) a floppy being mounted 195 * 196 * note: if an unmount fails we don't want to try 197 * to unmount the same device on the next try, so 198 * we try to watch for that 199 */ 200 201 do { 202 /* 203 * don't call the unmount program is we're just 204 * trying to unmount the same device that 205 * failed last time -- if that's the case, 206 * then bail 207 */ 208 if (spcl_failed != NULL) { 209 if (strcmp(spcl, spcl_failed) == 0) { 210 break; 211 } 212 } 213 ret_val = call_unmount_prog(TRUE, FALSE, 214 mtype, mnum, spcl, bn); 215 216 if (!ret_val) { 217 /* save spcl device name that failed */ 218 spcl_failed = strdup(spcl); 219 } else { 220 /* 221 * unmount succeeded, so clean up 222 */ 223 if (spcl_failed != NULL) { 224 free(spcl_failed); 225 spcl_failed = NULL; 226 } 227 } 228 229 } while (get_media_info(bn, &mtype, &mnum, &spcl)); 230 231 } else { 232 233 /* just do the unmmount cycle once */ 234 ret_val = call_unmount_prog(FALSE, FALSE, NULL, 0, 235 NULL, bn); 236 } 237 238 } 239 240 if (mtype != NULL) { 241 free(mtype); 242 } 243 if (spcl != NULL) { 244 free(spcl); 245 } 246 if (spcl_failed != NULL) { 247 free(spcl_failed); 248 } 249 if (bn != NULL) { 250 free(bn); 251 } 252 253 dun: 254 255 return (ret_val); 256 } 257 258 259 /* 260 * find a mnttab entry that has the same dev as the supplied dev, 261 * returning it and a non-zero value if found, else returning 0 262 * 263 * this is just like getmntany(), except that it scans based on st_rdev, 264 * and it even finds different slices on the same device/unit (thanx to 265 * code copied from format.c) 266 */ 267 static int 268 vol_getmntdev(FILE *fp, struct mnttab *mp, dev_t dev, struct dk_cinfo *ip) 269 { 270 int fd; /* dev-in-question fd */ 271 struct stat64 sb; /* dev-in-question stat struct */ 272 int ret_val = 0; /* default value: no match found */ 273 char *cn; /* char pathname */ 274 struct dk_cinfo dkinfo; /* for testing for slices */ 275 276 277 #ifdef DEBUG 278 denter( 279 "vol_getmntdev: entering for %d.%d, ctype/cnum/unit = %d/%d/%d\n", 280 (int)major(dev), (int)minor(dev), ip->dki_ctype, ip->dki_cnum, 281 ip->dki_unit); 282 #endif 283 284 /* reset the mnttab -- just in case */ 285 rewind(fp); 286 287 /* scan each entry in mnttab */ 288 while (getmntent(fp, mp) == 0) { 289 290 /* don't even try unless it's a local pathname */ 291 if (mp->mnt_special[0] != '/') { 292 continue; 293 } 294 295 /* get char pathname */ 296 if ((cn = volmgt_getfullrawname(mp->mnt_special)) == NULL) { 297 continue; 298 } 299 if (cn[0] == NULLC) { 300 free(cn); 301 continue; /* couldn't get raw name */ 302 } 303 304 /* open the device */ 305 if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) { 306 /* if we can't open it *assume* it's not a match */ 307 free(cn); 308 continue; 309 } 310 311 /* stat the device */ 312 if (fstat64(fd, &sb) < 0) { 313 free(cn); 314 (void) close(fd); 315 continue; /* ain't there: can't be a match */ 316 } 317 318 /* ensure we have a spcl device (a double check) */ 319 if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode)) { 320 free(cn); 321 (void) close(fd); 322 continue; 323 } 324 325 /* (almost) finally -- check the dev_t for equality */ 326 if (sb.st_rdev == dev) { 327 ret_val = 1; /* match found! */ 328 free(cn); 329 (void) close(fd); 330 break; 331 } 332 333 /* 334 * check that the major numbers match, since if they 335 * don't then there's no reason to use the DKIOCINFO 336 * ioctl to see if we have to major/minor pairs that 337 * really point to the same device 338 */ 339 if (major(sb.st_rdev) != major(dev)) { 340 /* no use continuing, since major devs are different */ 341 free(cn); 342 (void) close(fd); 343 continue; 344 } 345 346 /* one last check -- for diff. slices of the same dev/unit */ 347 if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) { 348 free(cn); 349 (void) close(fd); 350 continue; 351 } 352 353 free(cn); /* all done with raw pathname */ 354 (void) close(fd); /* all done with file descriptor */ 355 356 /* if ctrler type/number and unit match, it's a match */ 357 if ((ip->dki_ctype == dkinfo.dki_ctype) && 358 (ip->dki_cnum == dkinfo.dki_cnum) && 359 (ip->dki_unit == dkinfo.dki_unit)) { 360 /* 361 * even though minor numbers differ we have a 362 * match 363 */ 364 ret_val = 1; 365 break; 366 } 367 368 /* go around again */ 369 } 370 371 return (ret_val); 372 } 373 374 375 char * 376 vol_basename(char *path) 377 { 378 char *cp; 379 380 381 /* check for the degenerate case */ 382 if (strcmp(path, "/") == 0) { 383 return (path); 384 } 385 386 /* look for the last slash in the name */ 387 if ((cp = strrchr(path, '/')) == NULL) { 388 /* no slash */ 389 return (path); 390 } 391 392 /* ensure something is after the slash */ 393 if (*++cp != NULLC) { 394 return (cp); 395 } 396 397 /* a name that ends in slash -- back up until previous slash */ 398 while (cp != path) { 399 if (*--cp == '/') { 400 return (--cp); 401 } 402 } 403 404 /* the only slash is the end of the name */ 405 return (path); 406 } 407 408 409 static int 410 get_media_info(char *path, char **mtypep, int *mnump, char **spclp) 411 { 412 static int vol_getmntdev(FILE *, struct mnttab *, dev_t, 413 struct dk_cinfo *); 414 FILE *fp = NULL; 415 int fd = -1; 416 char *cn = NULL; /* char spcl pathname */ 417 struct stat64 sb; 418 struct dk_cinfo info; 419 struct mnttab mnt; 420 int ret_val = FALSE; 421 422 if ((fp = fopen(MNTTAB, "rF")) == NULL) { 423 /* mtab is gone... let him go */ 424 goto dun; 425 } 426 427 /* get char spcl pathname */ 428 if ((cn = volmgt_getfullrawname(path)) == NULL) { 429 goto dun; 430 } 431 if (cn[0] == NULLC) { 432 goto dun; 433 } 434 435 if ((fd = open(cn, O_RDONLY|O_NDELAY)) < 0) { 436 goto dun; 437 } 438 439 if (fstat64(fd, &sb) < 0) { 440 goto dun; 441 } 442 443 if (ioctl(fd, DKIOCINFO, &info) != 0) { 444 goto dun; 445 } 446 447 /* if we found the entry then disect it */ 448 if (vol_getmntdev(fp, &mnt, sb.st_rdev, &info) != 0) { 449 char *cp; 450 char *mtype; 451 char *mnt_dir; 452 int mtype_len; 453 DIR *dirp = NULL; 454 struct dirent64 *dp; 455 char *volname; 456 457 458 /* return the spcl device name found */ 459 *spclp = strdup(mnt.mnt_special); 460 461 /* 462 * try to get the media type (e.g. "floppy") from the mount 463 * point (e.g. "/floppy/NAME") if vold is running 464 */ 465 466 if (!volmgt_running() || 467 (!volmgt_ownspath(*spclp) && 468 volmgt_symname(*spclp) == NULL)) { 469 ret_val = TRUE; /* success (if limited) */ 470 goto dun; 471 } 472 473 /* get the first part of the mount point (e.g. "floppy") */ 474 cp = mnt.mnt_mountp; 475 if (*cp++ != '/') { 476 goto dun; 477 } 478 mtype = cp; 479 if ((cp = strchr(mtype, '/')) == NULL) { 480 goto dun; 481 } 482 *cp++ = NULLC; 483 mnt_dir = mnt.mnt_mountp; /* save dir path */ 484 485 /* get the volume name (e.g. "unnamed_floppy") */ 486 volname = cp; 487 488 /* scan for the symlink that points to our volname */ 489 if ((dirp = opendir(mnt_dir)) == NULL) { 490 goto dun; 491 } 492 mtype_len = strlen(mtype); 493 while ((dp = readdir64(dirp)) != NULL) { 494 char lpath[2 * (MAXNAMELEN+1)]; 495 char linkbuf[MAXPATHLEN+4]; 496 int lb_len; 497 struct stat64 sb; 498 499 500 if (strncmp(dp->d_name, mtype, mtype_len) != 0) { 501 continue; /* not even close */ 502 } 503 504 (void) sprintf(lpath, "%s/%s", mnt_dir, 505 dp->d_name); 506 if (lstat64(lpath, &sb) < 0) { 507 continue; /* what? */ 508 } 509 if (!S_ISLNK(sb.st_mode)) { 510 continue; /* not our baby */ 511 } 512 if ((lb_len = readlink(lpath, linkbuf, 513 sizeof (linkbuf))) < 0) { 514 continue; 515 } 516 linkbuf[lb_len] = NULLC; /* null terminate */ 517 if ((cp = vol_basename(linkbuf)) == NULL) { 518 continue; 519 } 520 /* now we have the name! */ 521 if (strcmp(cp, volname) == 0) { 522 /* found it !! */ 523 if (sscanf(dp->d_name + mtype_len, "%d", 524 mnump) == 1) { 525 *mtypep = strdup(mtype); 526 ret_val = TRUE; 527 } 528 break; 529 } 530 } 531 (void) closedir(dirp); 532 } 533 534 dun: 535 if (fp != NULL) { 536 (void) fclose(fp); 537 } 538 if (fd >= 0) { 539 (void) close(fd); 540 } 541 if (cn != NULL) { 542 free(cn); 543 } 544 #ifdef DEBUG 545 if (ret_val) { 546 dexit("get_media_info: returning mtype=%s, mnum=%d, spcl=%s\n", 547 *mtypep == NULL ? "<null ptr>" : *mtypep, 548 *mnump, 549 *spclp == NULL ? "<null ptr>" : *spclp); 550 } else { 551 dexit("get_media_info: FAILED\n"); 552 } 553 #endif 554 return (ret_val); 555 } 556 557 558 /* 559 * call the appropriate unmount program, returning its success (TRUE) 560 * or failure (FALSE) 561 */ 562 static int 563 call_unmount_prog(int mi_gotten, int use_rmm, char *mtype, int mnum, 564 char *spcl, char *bn) 565 { 566 pid_t pid; /* forked proc's pid */ 567 int ret_val = FALSE; 568 const char *etc_umount = "/etc/umount"; 569 const char *rmm = "/usr/sbin/rmmount"; 570 int rval; /* proc's return value */ 571 572 573 #ifdef DEBUG 574 denter( 575 "call_unmount_prog(%s, %s, \"%s\", %d, \"%s\", \"%s\"): entering\n", 576 mi_gotten ? "TRUE" : "FALSE", use_rmm ? "TRUE" : "FALSE", 577 mtype ? mtype : "<null ptr>", mnum, spcl ? spcl : "<null ptr>", 578 bn); 579 #endif 580 /* create a child to unmount the path */ 581 if ((pid = fork()) < 0) { 582 goto dun; 583 } 584 585 if (pid == 0) { 586 /* the child */ 587 #ifndef DEBUG 588 int xfd; 589 #endif 590 char env_buf[MAXPATHLEN]; 591 592 #ifndef DEBUG 593 /* get rid of those nasty err messages */ 594 if ((xfd = open(NULL_PATH, O_RDWR)) >= 0) { 595 (void) dup2(xfd, fileno(stdin)); 596 (void) dup2(xfd, fileno(stdout)); 597 (void) dup2(xfd, fileno(stderr)); 598 } 599 #endif 600 601 if (use_rmm) { 602 /* set up environment vars */ 603 (void) putenv("VOLUME_ACTION=eject"); 604 (void) putenv(strdup(env_buf)); 605 if (mi_gotten) { 606 (void) sprintf(env_buf, 607 "VOLUME_MEDIATYPE=%s", mtype); 608 (void) putenv(strdup(env_buf)); 609 (void) sprintf(env_buf, "VOLUME_SYMDEV=%s%d", 610 mtype, mnum); 611 (void) putenv(strdup(env_buf)); 612 (void) sprintf(env_buf, "VOLUME_PATH=%s", 613 spcl); 614 (void) putenv(strdup(env_buf)); 615 (void) sprintf(env_buf, "VOLUME_NAME=%s", 616 vol_basename(spcl)); 617 (void) putenv(strdup(env_buf)); 618 } else { 619 (void) sprintf(env_buf, "VOLUME_PATH=%s", bn); 620 (void) putenv(strdup(env_buf)); 621 (void) sprintf(env_buf, "VOLUME_NAME=%s", 622 vol_basename(bn)); 623 (void) putenv(strdup(env_buf)); 624 } 625 #ifdef DEBUG 626 dprintf("call_unmount_prog: calling \"%s -D\"\n", rmm); 627 (void) execl(rmm, rmm, "-D", NULL); 628 #else 629 (void) execl(rmm, rmm, NULL); 630 #endif 631 } else { 632 #ifdef DEBUG 633 dprintf("call_unmount_prog: calling \"%s %s\"\n", 634 etc_umount, mi_gotten ? spcl : bn); 635 #endif 636 (void) execl(etc_umount, etc_umount, 637 mi_gotten ? spcl : bn, 638 NULL); 639 } 640 exit(-1); 641 /*NOTREACHED*/ 642 } 643 644 /* wait for the umount command to exit */ 645 if (waitpid(pid, &rval, 0) == pid) { 646 if (WIFEXITED(rval)) { 647 if (WEXITSTATUS(rval) == 0) { 648 ret_val = TRUE; /* success */ 649 } 650 } 651 } 652 653 dun: 654 return (ret_val); 655 } 656