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