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