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