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 * autod_mount.c 23 * 24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include <stdio.h> 29 #include <ctype.h> 30 #include <string.h> 31 #include <syslog.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/param.h> 35 #include <errno.h> 36 #include <pwd.h> 37 #include <netinet/in.h> 38 #include <netdb.h> 39 #include <sys/tiuser.h> 40 #include <locale.h> 41 #include <stdlib.h> 42 #include <unistd.h> 43 #include <sys/mntent.h> 44 #include <sys/mnttab.h> 45 #include <sys/wait.h> 46 #include <sys/mount.h> 47 #include <sys/fs/autofs.h> 48 #include <nfs/nfs.h> 49 #include <thread.h> 50 #include <limits.h> 51 #include <assert.h> 52 #include <fcntl.h> 53 #include <strings.h> 54 55 #include "automount.h" 56 #include "replica.h" 57 58 static int unmount_mntpnt(struct mnttab *); 59 static int call_fork_exec(char *, char *, char **, int); 60 static void remove_browse_options(char *); 61 static int inherit_options(char *, char **); 62 63 int 64 do_mount1( 65 char *mapname, 66 char *key, 67 char *subdir, 68 char *mapopts, 69 char *path, 70 uint_t isdirect, 71 uid_t uid, 72 action_list **alpp, 73 int flags) 74 { 75 struct mapline ml; 76 struct mapent *me, *mapents = NULL; 77 char mntpnt[MAXPATHLEN]; 78 char spec_mntpnt[MAXPATHLEN]; 79 int err = 0; 80 char *private; /* fs specific data. eg prevhost in case of nfs */ 81 int mount_ok = 0; 82 ssize_t len; 83 action_list *alp, *prev, *tmp; 84 char root[MAXPATHLEN]; 85 int overlay = 1; 86 char next_subdir[MAXPATHLEN]; 87 bool_t mount_access = TRUE; 88 bool_t iswildcard; 89 bool_t isrestricted = hasrestrictopt(mapopts); 90 char *stack[STACKSIZ]; 91 char **stkptr = stack; 92 93 retry: 94 iswildcard = FALSE; 95 96 /* initialize the stack of open files for this thread */ 97 stack_op(INIT, NULL, stack, &stkptr); 98 99 err = getmapent(key, mapname, &ml, stack, &stkptr, &iswildcard, 100 isrestricted); 101 if (err == 0) { 102 mapents = parse_entry(key, mapname, mapopts, &ml, 103 subdir, isdirect, mount_access); 104 } 105 106 if (trace) { 107 struct mapfs *mfs; 108 trace_prt(1, " do_mount1:\n"); 109 for (me = mapents; me; me = me->map_next) { 110 trace_prt(1, " (%s,%s)\t%s%s%s\n", 111 me->map_fstype ? me->map_fstype : "", 112 me->map_mounter ? me->map_mounter : "", 113 path ? path : "", 114 me->map_root ? me->map_root : "", 115 me->map_mntpnt ? me->map_mntpnt : ""); 116 trace_prt(0, "\t\t-%s\n", 117 me->map_mntopts ? me->map_mntopts : ""); 118 119 for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next) 120 trace_prt(0, "\t\t%s:%s\tpenalty=%d\n", 121 mfs->mfs_host ? mfs->mfs_host: "", 122 mfs->mfs_dir ? mfs->mfs_dir : "", 123 mfs->mfs_penalty); 124 } 125 } 126 127 *alpp = NULL; 128 129 /* 130 * Each mapent in the list describes a mount to be done. 131 * Normally there's just a single entry, though in the 132 * case of /net mounts there may be many entries, that 133 * must be mounted as a hierarchy. For each mount the 134 * automountd must make sure the required mountpoint 135 * exists and invoke the appropriate mount command for 136 * the fstype. 137 */ 138 private = ""; 139 for (me = mapents; me && !err; me = me->map_next) { 140 len = snprintf(mntpnt, sizeof (mntpnt), "%s%s%s", path, 141 mapents->map_root, me->map_mntpnt); 142 143 if (len >= sizeof (mntpnt)) { 144 free_mapent(mapents); 145 return (ENAMETOOLONG); 146 } 147 /* 148 * remove trailing /'s from mountpoint to avoid problems 149 * stating a directory with two or more trailing slashes. 150 * This will let us mount directories from machines 151 * which export with two or more slashes (apollo for instance). 152 */ 153 len -= 1; 154 while (mntpnt[len] == '/') 155 mntpnt[len--] = '\0'; 156 157 (void) strcpy(spec_mntpnt, mntpnt); 158 159 if (isrestricted && 160 inherit_options(mapopts, &me->map_mntopts) != 0) { 161 syslog(LOG_ERR, "malloc of options failed"); 162 free_mapent(mapents); 163 return (EAGAIN); 164 } 165 166 if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) { 167 remove_browse_options(me->map_mntopts); 168 if (flags == DOMOUNT_KERNEL) { 169 alp = (action_list *)malloc( 170 sizeof (action_list)); 171 if (alp == NULL) { 172 syslog(LOG_ERR, 173 "malloc of alp failed"); 174 continue; 175 } 176 memset(alp, 0, sizeof (action_list)); 177 } else 178 alp = NULL; 179 err = 180 mount_nfs(me, spec_mntpnt, private, overlay, uid, 181 &alp); 182 /* 183 * We must retry if we don't have access to the 184 * root file system and there are other 185 * following mapents. The reason we can't 186 * continue because the rest of the mapent list 187 * depends on whether mount_access is TRUE or FALSE. 188 */ 189 if (err == NFSERR_ACCES && me->map_next != NULL) { 190 /* 191 * don't expect mount_access to be 192 * FALSE here, but we do a check 193 * anyway. 194 */ 195 if (mount_access == TRUE) { 196 mount_access = FALSE; 197 err = 0; 198 free_mapent(mapents); 199 if (alp) { 200 free(alp); 201 alp = NULL; 202 } 203 goto retry; 204 } 205 } 206 if (alp) { 207 if (*alpp == NULL) 208 *alpp = alp; 209 else { 210 for (tmp = *alpp; tmp != NULL; 211 tmp = tmp->next) 212 prev = tmp; 213 prev->next = alp; 214 } 215 } 216 mount_ok = !err; 217 } else if (strcmp(me->map_fstype, MNTTYPE_AUTOFS) == 0) { 218 if (isdirect) { 219 len = strlcpy(root, path, sizeof (root)); 220 } else { 221 len = snprintf(root, sizeof (root), "%s/%s", 222 path, key); 223 } 224 if (len >= sizeof (root)) { 225 free_mapent(mapents); 226 return (ENAMETOOLONG); 227 } 228 229 alp = (action_list *)malloc(sizeof (action_list)); 230 if (alp == NULL) { 231 syslog(LOG_ERR, "malloc of alp failed"); 232 continue; 233 } 234 memset(alp, 0, sizeof (action_list)); 235 236 /* 237 * get the next subidr, but only if its a modified 238 * or faked autofs mount 239 */ 240 if (me->map_modified || me->map_faked) { 241 len = snprintf(next_subdir, 242 sizeof (next_subdir), "%s%s", subdir, 243 me->map_mntpnt); 244 } else { 245 next_subdir[0] = '\0'; 246 len = 0; 247 } 248 249 if (trace > 2) 250 trace_prt(1, " root=%s\t next_subdir=%s\n", 251 root, next_subdir); 252 if (len < sizeof (next_subdir)) { 253 err = mount_autofs(me, spec_mntpnt, alp, 254 root, next_subdir, key); 255 } else { 256 err = ENAMETOOLONG; 257 } 258 if (err == 0) { 259 /* 260 * append to action list 261 */ 262 mount_ok++; 263 if (*alpp == NULL) 264 *alpp = alp; 265 else { 266 for (tmp = *alpp; tmp != NULL; 267 tmp = tmp->next) 268 prev = tmp; 269 prev->next = alp; 270 } 271 } else { 272 free(alp); 273 mount_ok = 0; 274 } 275 } else if (strcmp(me->map_fstype, MNTTYPE_LOFS) == 0) { 276 remove_browse_options(me->map_mntopts); 277 err = loopbackmount(me->map_fs->mfs_dir, spec_mntpnt, 278 me->map_mntopts, overlay); 279 mount_ok = !err; 280 } else { 281 remove_browse_options(me->map_mntopts); 282 err = mount_generic(me->map_fs->mfs_dir, 283 me->map_fstype, me->map_mntopts, 284 spec_mntpnt, overlay); 285 mount_ok = !err; 286 } 287 } 288 if (mapents) 289 free_mapent(mapents); 290 291 /* 292 * If an error occurred, 293 * the filesystem doesn't exist, or could not be 294 * mounted. Return EACCES to autofs indicating that 295 * the mountpoint can not be accessed if this is not 296 * a wildcard access. If it is a wildcard access we 297 * return ENOENT since the lookup that triggered 298 * this mount request will fail and the entry will not 299 * be available. 300 */ 301 if (mount_ok) { 302 /* 303 * No error occurred, return 0 to indicate success. 304 */ 305 err = 0; 306 } else { 307 /* 308 * The filesystem does not exist or could not be mounted. 309 * Return ENOENT if the lookup was triggered by a wildcard 310 * access. Wildcard entries only exist if they can be 311 * mounted. They can not be listed otherwise (through 312 * a readdir(2)). 313 * Return EACCES if the lookup was not triggered by a 314 * wildcard access. Map entries that are explicitly defined 315 * in maps are visible via readdir(2), therefore we return 316 * EACCES to indicate that the entry exists, but the directory 317 * can not be opened. This is the same behavior of a Unix 318 * directory that exists, but has its execute bit turned off. 319 * The directory is there, but the user does not have access 320 * to it. 321 */ 322 if (iswildcard) 323 err = ENOENT; 324 else 325 err = EACCES; 326 } 327 return (err); 328 } 329 330 #define ARGV_MAX 16 331 #define VFS_PATH "/usr/lib/fs" 332 333 int 334 mount_generic(special, fstype, opts, mntpnt, overlay) 335 char *special, *fstype, *opts, *mntpnt; 336 int overlay; 337 { 338 struct mnttab m; 339 struct stat stbuf; 340 int i, res; 341 char *newargv[ARGV_MAX]; 342 343 if (trace > 1) { 344 trace_prt(1, " mount: %s %s %s %s\n", 345 special, mntpnt, fstype, opts); 346 } 347 348 if (stat(mntpnt, &stbuf) < 0) { 349 syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt); 350 return (ENOENT); 351 } 352 353 i = 2; 354 355 if (overlay) 356 newargv[i++] = "-O"; 357 358 /* 359 * Use "quiet" option to suppress warnings about unsupported 360 * mount options. 361 */ 362 newargv[i++] = "-q"; 363 364 if (opts && *opts) { 365 m.mnt_mntopts = opts; 366 if (hasmntopt(&m, MNTOPT_RO) != NULL) 367 newargv[i++] = "-r"; 368 newargv[i++] = "-o"; 369 newargv[i++] = opts; 370 } 371 newargv[i++] = "--"; 372 newargv[i++] = special; 373 newargv[i++] = mntpnt; 374 newargv[i] = NULL; 375 res = call_fork_exec(fstype, "mount", newargv, verbose); 376 if (res == 0 && trace > 1) { 377 if (stat(mntpnt, &stbuf) == 0) { 378 trace_prt(1, " mount of %s dev=%x rdev=%x OK\n", 379 mntpnt, stbuf.st_dev, stbuf.st_rdev); 380 } else { 381 trace_prt(1, " failed to stat %s\n", mntpnt); 382 } 383 } 384 return (res); 385 } 386 387 void 388 automountd_do_fork_exec(void *cookie, char *argp, size_t arg_size, 389 door_desc_t *dfd, uint_t n_desc) 390 { 391 int stat_loc; 392 int fd = 0; 393 struct stat stbuf; 394 int res; 395 int child_pid; 396 command_t *command; 397 char *newargv[ARGV_MAX]; 398 int i; 399 400 401 command = (command_t *)argp; 402 if (sizeof (*command) != arg_size) { 403 res = EINVAL; 404 door_return((char *)&res, sizeof (res), NULL, 0); 405 } 406 407 switch ((child_pid = fork1())) { 408 case -1: 409 syslog(LOG_ERR, "Cannot fork: %m"); 410 res = errno; 411 break; 412 case 0: 413 /* 414 * Child 415 */ 416 (void) setsid(); 417 fd = open(command->console ? "/dev/console" : "/dev/null", 418 O_WRONLY); 419 if (fd != -1) { 420 (void) dup2(fd, 1); 421 (void) dup2(fd, 2); 422 (void) close(fd); 423 } 424 425 for (i = 0; *command->argv[i]; i++) { 426 newargv[i] = strdup(command->argv[i]); 427 if (newargv[i] == (char *)NULL) { 428 syslog(LOG_ERR, "failed to copy argument '%s'" 429 " of %s: %m", command->argv[i], 430 command->file); 431 _exit(errno); 432 } 433 } 434 newargv[i] = NULL; 435 436 (void) execv(command->file, newargv); 437 if (errno == EACCES) 438 syslog(LOG_ERR, "exec %s: %m", command->file); 439 440 _exit(errno); 441 default: 442 /* 443 * Parent 444 */ 445 (void) waitpid(child_pid, &stat_loc, WUNTRACED); 446 447 if (WIFEXITED(stat_loc)) { 448 if (trace > 1) { 449 trace_prt(1, 450 " fork_exec: returns exit status %d\n", 451 WEXITSTATUS(stat_loc)); 452 } 453 454 res = WEXITSTATUS(stat_loc); 455 } else if (WIFSIGNALED(stat_loc)) { 456 if (trace > 1) 457 trace_prt(1, 458 " fork_exec: returns signal status %d\n", 459 WTERMSIG(stat_loc)); 460 res = 1; 461 } else { 462 if (trace > 1) 463 trace_prt(1, 464 " fork_exec: returns unknown status\n"); 465 res = 1; 466 } 467 468 } 469 door_return((char *)&res, sizeof (res), NULL, 0); 470 trace_prt(1, "automountd_do_fork_exec, door return failed %s, %s\n", 471 command->file, strerror(errno)); 472 door_return(NULL, 0, NULL, 0); 473 } 474 475 int 476 do_unmount1(ur) 477 umntrequest *ur; 478 { 479 480 struct mnttab m; 481 int res = 0; 482 483 m.mnt_special = ur->mntresource; 484 m.mnt_mountp = ur->mntpnt; 485 m.mnt_fstype = ur->fstype; 486 m.mnt_mntopts = ur->mntopts; 487 /* 488 * Special case for NFS mounts. 489 * Don't want to attempt unmounts from 490 * a dead server. If any member of a 491 * hierarchy belongs to a dead server 492 * give up (try later). 493 */ 494 if (strcmp(ur->fstype, MNTTYPE_NFS) == 0) { 495 struct replica *list; 496 int i, n; 497 bool_t pubopt = FALSE; 498 int nfs_port; 499 int got_port; 500 501 /* 502 * See if a port number was specified. If one was 503 * specified that is too large to fit in 16 bits, truncate 504 * the high-order bits (for historical compatibility). Use 505 * zero to indicate "no port specified". 506 */ 507 got_port = nopt(&m, MNTOPT_PORT, &nfs_port); 508 if (!got_port) 509 nfs_port = 0; 510 nfs_port &= USHRT_MAX; 511 512 if (hasmntopt(&m, MNTOPT_PUBLIC)) 513 pubopt = TRUE; 514 515 list = parse_replica(ur->mntresource, &n); 516 if (list == NULL) { 517 if (n >= 0) 518 syslog(LOG_ERR, "Memory allocation failed: %m"); 519 res = 1; 520 goto done; 521 } 522 523 for (i = 0; i < n; i++) { 524 if (pingnfs(list[i].host, 1, NULL, 0, nfs_port, 525 pubopt, list[i].path, NULL) != RPC_SUCCESS) { 526 res = 1; 527 free_replica(list, n); 528 goto done; 529 } 530 } 531 free_replica(list, n); 532 } 533 534 res = unmount_mntpnt(&m); 535 536 done: return (res); 537 } 538 539 static int 540 unmount_mntpnt(mnt) 541 struct mnttab *mnt; 542 { 543 char *fstype = mnt->mnt_fstype; 544 char *mountp = mnt->mnt_mountp; 545 char *newargv[ARGV_MAX]; 546 int res; 547 548 if (strcmp(fstype, MNTTYPE_NFS) == 0) { 549 res = nfsunmount(mnt); 550 } else if (strcmp(fstype, MNTTYPE_LOFS) == 0) { 551 if ((res = umount(mountp)) < 0) 552 res = errno; 553 } else { 554 newargv[2] = mountp; 555 newargv[3] = NULL; 556 557 res = call_fork_exec(fstype, "umount", newargv, verbose); 558 if (res == ENOENT) { 559 /* 560 * filesystem specific unmount command not found 561 */ 562 if ((res = umount(mountp)) < 0) 563 res = errno; 564 } 565 } 566 567 if (trace > 1) 568 trace_prt(1, " unmount %s %s\n", 569 mountp, res ? "failed" : "OK"); 570 return (res); 571 } 572 573 /* 574 * Remove the autofs specific options 'browse', 'nobrowse' and 575 * 'restrict' from 'opts'. 576 */ 577 static void 578 remove_browse_options(char *opts) 579 { 580 char *p, *pb; 581 char buf[MAXOPTSLEN], new[MAXOPTSLEN]; 582 char *placeholder; 583 584 new[0] = '\0'; 585 (void) strcpy(buf, opts); 586 pb = buf; 587 while (p = (char *)strtok_r(pb, ",", &placeholder)) { 588 pb = NULL; 589 if (strcmp(p, MNTOPT_NOBROWSE) != 0 && 590 strcmp(p, MNTOPT_BROWSE) != 0 && 591 strcmp(p, MNTOPT_RESTRICT) != 0) { 592 if (new[0] != '\0') 593 (void) strcat(new, ","); 594 (void) strcat(new, p); 595 } 596 } 597 (void) strcpy(opts, new); 598 } 599 600 static const char *restropts[] = { 601 RESTRICTED_MNTOPTS 602 }; 603 #define NROPTS (sizeof (restropts)/sizeof (restropts[0])) 604 605 static int 606 inherit_options(char *opts, char **mapentopts) 607 { 608 int i; 609 char *new; 610 struct mnttab mtmap; 611 struct mnttab mtopt; 612 613 size_t len = strlen(*mapentopts); 614 615 for (i = 0; i < NROPTS; i++) 616 len += strlen(restropts[i]); 617 618 /* "," for each new option plus the trailing NUL */ 619 len += NROPTS + 1; 620 621 new = malloc(len); 622 if (new == 0) 623 return (-1); 624 625 (void) strcpy(new, *mapentopts); 626 627 mtmap.mnt_mntopts = *mapentopts; 628 mtopt.mnt_mntopts = opts; 629 630 for (i = 0; i < NROPTS; i++) { 631 if (hasmntopt(&mtopt, (char *)restropts[i]) != NULL && 632 hasmntopt(&mtmap, (char *)restropts[i]) == NULL) { 633 if (*new != '\0') 634 (void) strcat(new, ","); 635 (void) strcat(new, restropts[i]); 636 } 637 } 638 free(*mapentopts); 639 *mapentopts = new; 640 return (0); 641 } 642 643 bool_t 644 hasrestrictopt(char *opts) 645 { 646 struct mnttab mt; 647 648 mt.mnt_mntopts = opts; 649 650 return (hasmntopt(&mt, MNTOPT_RESTRICT) != NULL); 651 } 652 653 static int 654 call_fork_exec(fstype, cmd, newargv, console) 655 char *fstype; 656 char *cmd; 657 char **newargv; 658 int console; 659 { 660 command_t command; 661 door_arg_t darg; 662 char path[MAXPATHLEN]; 663 struct stat stbuf; 664 int ret; 665 int sz; 666 int status; 667 int i; 668 669 bzero(&command, sizeof (command)); 670 /* build the full path name of the fstype dependent command */ 671 (void) snprintf(path, MAXPATHLEN, "%s/%s/%s", VFS_PATH, fstype, cmd); 672 673 if (stat(path, &stbuf) != 0) { 674 ret = errno; 675 return (ret); 676 } 677 678 strlcpy(command.file, path, MAXPATHLEN); 679 strlcpy(command.argv[0], path, MAXOPTSLEN); 680 for (i = 2; newargv[i]; i++) { 681 strlcpy(command.argv[i-1], newargv[i], MAXOPTSLEN); 682 } 683 if (trace > 1) { 684 trace_prt(1, " call_fork_exec: %s ", command.file); 685 for (i = 0; *command.argv[i]; i++) 686 trace_prt(0, "%s ", command.argv[i]); 687 trace_prt(0, "\n"); 688 } 689 690 command.console = console; 691 692 darg.data_ptr = (char *)&command; 693 darg.data_size = sizeof (command); 694 darg.desc_ptr = NULL; 695 darg.desc_num = 0; 696 darg.rbuf = (char *)&status; 697 darg.rsize = sizeof (status); 698 699 ret = door_call(did_fork_exec, &darg); 700 if (trace > 1) { 701 trace_prt(1, " call_fork_exec: door_call failed %d\n", ret); 702 } 703 704 return (status); 705 } 706