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