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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * autod_mount.c 24 * 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <stdio.h> 32 #include <ctype.h> 33 #include <string.h> 34 #include <syslog.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <sys/param.h> 38 #include <errno.h> 39 #include <pwd.h> 40 #include <netinet/in.h> 41 #include <netdb.h> 42 #include <sys/tiuser.h> 43 #include <locale.h> 44 #include <stdlib.h> 45 #include <unistd.h> 46 #include <sys/mntent.h> 47 #include <sys/mnttab.h> 48 #include <sys/wait.h> 49 #include <sys/mount.h> 50 #include <sys/fs/autofs.h> 51 #include <nfs/nfs.h> 52 #include <thread.h> 53 #include <limits.h> 54 #include <assert.h> 55 #include <fcntl.h> 56 57 #include "automount.h" 58 #include "replica.h" 59 60 static int unmount_mntpnt(struct mnttab *); 61 static int 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(mapname, key, subdir, mapopts, path, isdirect, alpp, cred) 67 char *mapname; 68 char *key; 69 char *subdir; 70 char *mapopts; 71 char *path; 72 uint_t isdirect; 73 struct action_list **alpp; 74 struct authunix_parms *cred; 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 > 1) { 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 err = 170 mount_nfs(me, spec_mntpnt, private, overlay, cred); 171 /* 172 * We must retry if we don't have access to the 173 * root file system and there are other 174 * following mapents. The reason we can't 175 * continue because the rest of the mapent list 176 * depends on whether mount_access is TRUE or FALSE. 177 */ 178 if (err == NFSERR_ACCES && me->map_next != NULL) { 179 /* 180 * don't expect mount_access to be 181 * FALSE here, but we do a check 182 * anyway. 183 */ 184 if (mount_access == TRUE) { 185 mount_access = FALSE; 186 err = 0; 187 free_mapent(mapents); 188 goto retry; 189 } 190 } 191 mount_ok = !err; 192 } else if (strcmp(me->map_fstype, MNTTYPE_AUTOFS) == 0) { 193 if (isdirect) { 194 len = strlcpy(root, path, sizeof (root)); 195 } else { 196 len = snprintf(root, sizeof (root), "%s/%s", 197 path, key); 198 } 199 if (len >= sizeof (root)) { 200 free_mapent(mapents); 201 return (ENAMETOOLONG); 202 } 203 204 alp = (action_list *)malloc(sizeof (action_list)); 205 if (alp == NULL) { 206 syslog(LOG_ERR, "malloc of alp failed"); 207 continue; 208 } 209 memset(alp, 0, sizeof (action_list)); 210 211 /* 212 * get the next subidr, but only if its a modified 213 * or faked autofs mount 214 */ 215 if (me->map_modified || me->map_faked) { 216 len = snprintf(next_subdir, 217 sizeof (next_subdir), "%s%s", subdir, 218 me->map_mntpnt); 219 } else { 220 next_subdir[0] = '\0'; 221 len = 0; 222 } 223 224 if (trace > 2) 225 trace_prt(1, " root=%s\t next_subdir=%s\n", 226 root, next_subdir); 227 if (len < sizeof (next_subdir)) { 228 err = mount_autofs(me, spec_mntpnt, alp, 229 root, next_subdir, key); 230 } else { 231 err = ENAMETOOLONG; 232 } 233 if (err == 0) { 234 /* 235 * append to action list 236 */ 237 mount_ok++; 238 if (*alpp == NULL) 239 *alpp = alp; 240 else { 241 for (tmp = *alpp; tmp != NULL; 242 tmp = tmp->next) 243 prev = tmp; 244 prev->next = alp; 245 } 246 } else { 247 free(alp); 248 mount_ok = 0; 249 } 250 } else if (strcmp(me->map_fstype, MNTTYPE_LOFS) == 0) { 251 remove_browse_options(me->map_mntopts); 252 err = loopbackmount(me->map_fs->mfs_dir, spec_mntpnt, 253 me->map_mntopts, overlay); 254 mount_ok = !err; 255 } else { 256 remove_browse_options(me->map_mntopts); 257 err = mount_generic(me->map_fs->mfs_dir, 258 me->map_fstype, me->map_mntopts, 259 spec_mntpnt, overlay); 260 mount_ok = !err; 261 } 262 } 263 if (mapents) 264 free_mapent(mapents); 265 266 /* 267 * If an error occurred, 268 * the filesystem doesn't exist, or could not be 269 * mounted. Return EACCES to autofs indicating that 270 * the mountpoint can not be accessed if this is not 271 * a wildcard access. If it is a wildcard access we 272 * return ENOENT since the lookup that triggered 273 * this mount request will fail and the entry will not 274 * be available. 275 */ 276 if (mount_ok) { 277 /* 278 * No error occurred, return 0 to indicate success. 279 */ 280 err = 0; 281 } else { 282 /* 283 * The filesystem does not exist or could not be mounted. 284 * Return ENOENT if the lookup was triggered by a wildcard 285 * access. Wildcard entries only exist if they can be 286 * mounted. They can not be listed otherwise (through 287 * a readdir(2)). 288 * Return EACCES if the lookup was not triggered by a 289 * wildcard access. Map entries that are explicitly defined 290 * in maps are visible via readdir(2), therefore we return 291 * EACCES to indicate that the entry exists, but the directory 292 * can not be opened. This is the same behavior of a Unix 293 * directory that exists, but has its execute bit turned off. 294 * The directory is there, but the user does not have access 295 * to it. 296 */ 297 if (iswildcard) 298 err = ENOENT; 299 else 300 err = EACCES; 301 } 302 return (err); 303 } 304 305 #define ARGV_MAX 16 306 #define VFS_PATH "/usr/lib/fs" 307 308 int 309 mount_generic(special, fstype, opts, mntpnt, overlay) 310 char *special, *fstype, *opts, *mntpnt; 311 int overlay; 312 { 313 struct mnttab m; 314 struct stat stbuf; 315 int i, res; 316 char *newargv[ARGV_MAX]; 317 318 if (trace > 1) { 319 trace_prt(1, " mount: %s %s %s %s\n", 320 special, mntpnt, fstype, opts); 321 } 322 323 if (stat(mntpnt, &stbuf) < 0) { 324 syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt); 325 return (ENOENT); 326 } 327 328 i = 2; 329 330 if (overlay) 331 newargv[i++] = "-O"; 332 333 /* 334 * Use "quiet" option to suppress warnings about unsupported 335 * mount options. 336 */ 337 newargv[i++] = "-q"; 338 339 if (opts && *opts) { 340 m.mnt_mntopts = opts; 341 if (hasmntopt(&m, MNTOPT_RO) != NULL) 342 newargv[i++] = "-r"; 343 newargv[i++] = "-o"; 344 newargv[i++] = opts; 345 } 346 newargv[i++] = "--"; 347 newargv[i++] = special; 348 newargv[i++] = mntpnt; 349 newargv[i] = NULL; 350 res = fork_exec(fstype, "mount", newargv, verbose); 351 if (res == 0 && trace > 1) { 352 if (stat(mntpnt, &stbuf) == 0) { 353 trace_prt(1, " mount of %s dev=%x rdev=%x OK\n", 354 mntpnt, stbuf.st_dev, stbuf.st_rdev); 355 } else { 356 trace_prt(1, " failed to stat %s\n", mntpnt); 357 } 358 } 359 return (res); 360 } 361 362 static int 363 fork_exec(fstype, cmd, newargv, console) 364 char *fstype; 365 char *cmd; 366 char **newargv; 367 int console; 368 { 369 char path[MAXPATHLEN]; 370 int i; 371 int stat_loc; 372 int fd = 0; 373 struct stat stbuf; 374 int res; 375 int child_pid; 376 377 /* build the full path name of the fstype dependent command */ 378 (void) sprintf(path, "%s/%s/%s", VFS_PATH, fstype, cmd); 379 380 if (stat(path, &stbuf) != 0) { 381 res = errno; 382 return (res); 383 } 384 385 if (trace > 1) { 386 trace_prt(1, " fork_exec: %s ", path); 387 for (i = 2; newargv[i]; i++) 388 trace_prt(0, "%s ", newargv[i]); 389 trace_prt(0, "\n"); 390 } 391 392 393 newargv[1] = cmd; 394 switch ((child_pid = fork1())) { 395 case -1: 396 syslog(LOG_ERR, "Cannot fork: %m"); 397 return (errno); 398 case 0: 399 /* 400 * Child 401 */ 402 (void) setsid(); 403 fd = open(console ? "/dev/console" : "/dev/null", O_WRONLY); 404 if (fd != -1) { 405 (void) dup2(fd, 1); 406 (void) dup2(fd, 2); 407 (void) close(fd); 408 } 409 410 (void) execv(path, &newargv[1]); 411 if (errno == EACCES) 412 syslog(LOG_ERR, "exec %s: %m", path); 413 414 _exit(errno); 415 default: 416 /* 417 * Parent 418 */ 419 (void) waitpid(child_pid, &stat_loc, WUNTRACED); 420 421 if (WIFEXITED(stat_loc)) { 422 if (trace > 1) { 423 trace_prt(1, 424 " fork_exec: returns exit status %d\n", 425 WEXITSTATUS(stat_loc)); 426 } 427 428 return (WEXITSTATUS(stat_loc)); 429 } else 430 if (WIFSIGNALED(stat_loc)) { 431 if (trace > 1) { 432 trace_prt(1, 433 " fork_exec: returns signal status %d\n", 434 WTERMSIG(stat_loc)); 435 } 436 } else { 437 if (trace > 1) 438 trace_prt(1, 439 " fork_exec: returns unknown status\n"); 440 } 441 442 return (1); 443 } 444 } 445 446 int 447 do_unmount1(ur) 448 umntrequest *ur; 449 { 450 451 struct mnttab m; 452 int res = 0; 453 454 m.mnt_special = ur->mntresource; 455 m.mnt_mountp = ur->mntpnt; 456 m.mnt_fstype = ur->fstype; 457 m.mnt_mntopts = ur->mntopts; 458 /* 459 * Special case for NFS mounts. 460 * Don't want to attempt unmounts from 461 * a dead server. If any member of a 462 * hierarchy belongs to a dead server 463 * give up (try later). 464 */ 465 if (strcmp(ur->fstype, MNTTYPE_NFS) == 0) { 466 struct replica *list; 467 int i, n; 468 bool_t pubopt = FALSE; 469 int nfs_port; 470 int got_port; 471 472 /* 473 * See if a port number was specified. If one was 474 * specified that is too large to fit in 16 bits, truncate 475 * the high-order bits (for historical compatibility). Use 476 * zero to indicate "no port specified". 477 */ 478 got_port = nopt(&m, MNTOPT_PORT, &nfs_port); 479 if (!got_port) 480 nfs_port = 0; 481 nfs_port &= USHRT_MAX; 482 483 if (hasmntopt(&m, MNTOPT_PUBLIC)) 484 pubopt = TRUE; 485 486 list = parse_replica(ur->mntresource, &n); 487 if (list == NULL) { 488 if (n >= 0) 489 syslog(LOG_ERR, "Memory allocation failed: %m"); 490 res = 1; 491 goto done; 492 } 493 494 for (i = 0; i < n; i++) { 495 if (pingnfs(list[i].host, 1, NULL, 0, nfs_port, 496 pubopt, list[i].path, NULL) != RPC_SUCCESS) { 497 res = 1; 498 free_replica(list, n); 499 goto done; 500 } 501 } 502 free_replica(list, n); 503 } 504 505 res = unmount_mntpnt(&m); 506 507 done: return (res); 508 } 509 510 static int 511 unmount_mntpnt(mnt) 512 struct mnttab *mnt; 513 { 514 char *fstype = mnt->mnt_fstype; 515 char *mountp = mnt->mnt_mountp; 516 char *newargv[ARGV_MAX]; 517 int res; 518 519 if (strcmp(fstype, MNTTYPE_NFS) == 0) { 520 res = nfsunmount(mnt); 521 } else if (strcmp(fstype, MNTTYPE_LOFS) == 0) { 522 if ((res = umount(mountp)) < 0) 523 res = errno; 524 } else { 525 newargv[2] = mountp; 526 newargv[3] = NULL; 527 528 res = fork_exec(fstype, "umount", newargv, verbose); 529 if (res == ENOENT) { 530 /* 531 * filesystem specific unmount command not found 532 */ 533 if ((res = umount(mountp)) < 0) 534 res = errno; 535 } 536 } 537 538 if (trace > 1) 539 trace_prt(1, " unmount %s %s\n", 540 mountp, res ? "failed" : "OK"); 541 return (res); 542 } 543 544 /* 545 * Remove the autofs specific options 'browse', 'nobrowse' and 546 * 'restrict' from 'opts'. 547 */ 548 static void 549 remove_browse_options(char *opts) 550 { 551 char *p, *pb; 552 char buf[MAXOPTSLEN], new[MAXOPTSLEN]; 553 char *placeholder; 554 555 new[0] = '\0'; 556 (void) strcpy(buf, opts); 557 pb = buf; 558 while (p = (char *)strtok_r(pb, ",", &placeholder)) { 559 pb = NULL; 560 if (strcmp(p, MNTOPT_NOBROWSE) != 0 && 561 strcmp(p, MNTOPT_BROWSE) != 0 && 562 strcmp(p, MNTOPT_RESTRICT) != 0) { 563 if (new[0] != '\0') 564 (void) strcat(new, ","); 565 (void) strcat(new, p); 566 } 567 } 568 (void) strcpy(opts, new); 569 } 570 571 static const char *restropts[] = { 572 RESTRICTED_MNTOPTS 573 }; 574 #define NROPTS (sizeof (restropts)/sizeof (restropts[0])) 575 576 static int 577 inherit_options(char *opts, char **mapentopts) 578 { 579 int i; 580 char *new; 581 struct mnttab mtmap; 582 struct mnttab mtopt; 583 584 size_t len = strlen(*mapentopts); 585 586 for (i = 0; i < NROPTS; i++) 587 len += strlen(restropts[i]); 588 589 /* "," for each new option plus the trailing NUL */ 590 len += NROPTS + 1; 591 592 new = malloc(len); 593 if (new == 0) 594 return (-1); 595 596 (void) strcpy(new, *mapentopts); 597 598 mtmap.mnt_mntopts = *mapentopts; 599 mtopt.mnt_mntopts = opts; 600 601 for (i = 0; i < NROPTS; i++) { 602 if (hasmntopt(&mtopt, (char *)restropts[i]) != NULL && 603 hasmntopt(&mtmap, (char *)restropts[i]) == NULL) { 604 if (*new != '\0') 605 (void) strcat(new, ","); 606 (void) strcat(new, restropts[i]); 607 } 608 } 609 free(*mapentopts); 610 *mapentopts = new; 611 return (0); 612 } 613 614 bool_t 615 hasrestrictopt(char *opts) 616 { 617 struct mnttab mt; 618 619 mt.mnt_mntopts = opts; 620 621 return (hasmntopt(&mt, MNTOPT_RESTRICT) != NULL); 622 } 623