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_autofs.c 24 * 25 * Copyright (c) 1988-1996 Sun Microsystems Inc 26 * All Rights Reserved. 27 */ 28 29/* 30 * Portions Copyright 2007-2012 Apple Inc. 31 */ 32 33#pragma ident "@(#)autod_autofs.c 1.27 05/06/08 SMI" 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <ctype.h> 38#include <sys/param.h> 39#include <mntopts.h> 40#include <syslog.h> 41#include <string.h> 42#include <errno.h> 43#include <assert.h> 44#include "automount.h" 45 46static int process_opts(char *options, uint32_t *directp); 47 48static const struct mntopt mopts_autofs[] = { 49 MOPT_STDOPTS, 50 { MNTOPT_RESTRICT, 0, AUTOFS_MNT_RESTRICT, 1 }, 51 { "browse", 1, AUTOFS_MNT_NOBROWSE, 1 }, 52 { MNTOPT_HIDEFROMFINDER, 0, AUTOFS_MNT_HIDEFROMFINDER, 1 }, 53 { NULL, 0, 0, 0 } 54}; 55 56int 57mount_autofs( 58 const char *mapname, 59 struct mapent *me, 60 const char *mntpnt, 61 fsid_t mntpnt_fsid, 62 action_list **alpp, 63 const char *rootp, 64 const char *subdir, 65 const char *key, 66 fsid_t *fsidp, 67 uint32_t *retflags 68) 69{ 70 action_list *alp; 71 char rel_mntpnt[MAXPATHLEN]; 72 const char *trig_mntpnt; 73 mntoptparse_t mp; 74 int error; 75 76 if (trace > 1) 77 trace_prt(1, " mount_autofs %s on %s\n", 78 me->map_fs->mfs_dir, mntpnt); 79 80 if (strcmp(mntpnt, "/-") == 0) { 81 syslog(LOG_ERR, "invalid mountpoint: /-"); 82 *alpp = NULL; 83 return (ENOENT); 84 } 85 86 /* 87 * get relative mountpoint 88 */ 89 if (snprintf(rel_mntpnt, sizeof (rel_mntpnt), ".%s", 90 mntpnt+strlen(rootp)) >= (int)sizeof (rel_mntpnt)) { 91 syslog(LOG_ERR, "mountpoint too long: %s", mntpnt); 92 *alpp = NULL; 93 return (ENOENT); 94 } 95 96 if (trace > 2) 97 trace_prt(1, " rel_mntpnt = %s\n", rel_mntpnt); 98 99 /* 100 * Get the mount point for this autofs mount relative to the 101 * mount point for the file system atop which we're mounting it. 102 */ 103 trig_mntpnt = me->map_mntpnt; 104 while (*trig_mntpnt == '/') 105 trig_mntpnt++; 106 107 alp = (action_list *)malloc(sizeof (action_list)); 108 if (alp == NULL) { 109 syslog(LOG_ERR, "malloc of alp failed"); 110 *alpp = NULL; 111 return (ENOMEM); 112 } 113 memset(alp, 0, sizeof (action_list)); 114 115 if ((alp->mounta.opts = malloc(AUTOFS_MAXOPTSLEN)) == NULL) 116 goto free_mem; 117 if (strlcpy(alp->mounta.opts, me->map_mntopts, AUTOFS_MAXOPTSLEN) 118 >= AUTOFS_MAXOPTSLEN) { 119 syslog(LOG_ERR, "options \"%s\" for %s are too long", 120 me->map_mntopts, mntpnt); 121 free(alp->mounta.opts); 122 free(alp); 123 *alpp = NULL; 124 return (ENOENT); 125 } 126 127 if (process_opts(alp->mounta.opts, &alp->mounta.isdirect) != 0) 128 goto free_mem; 129 130 /* 131 * get absolute mountpoint 132 */ 133 if ((alp->mounta.path = strdup(mntpnt)) == NULL) 134 goto free_mem; 135 136 if ((alp->mounta.map = strdup(me->map_fs->mfs_dir)) == NULL) 137 goto free_mem; 138 if ((alp->mounta.subdir = strdup(subdir)) == NULL) 139 goto free_mem; 140 141 if (alp->mounta.isdirect) { 142 if (me->map_modified == TRUE || me->map_faked == TRUE) { 143 if ((alp->mounta.key = strdup(key)) == NULL) 144 goto free_mem; 145 } else { 146 /* wierd case of a direct map pointer in another map */ 147 if ((alp->mounta.key = strdup(alp->mounta.path)) == NULL) 148 goto free_mem; 149 } 150 } else { 151 alp->mounta.key = NULL; 152 } 153 154 /* 155 * Fill out action list. 156 */ 157 if ((alp->mounta.dir = strdup(rel_mntpnt)) == NULL) 158 goto free_mem; 159 if ((alp->mounta.trig_mntpnt = strdup(trig_mntpnt)) == NULL) 160 goto free_mem; 161 162 /* 163 * Parse the mount options and fill in "flags" and "mntflags". 164 */ 165 alp->mounta.flags = alp->mounta.mntflags = 0; 166 getmnt_silent = 1; 167 mp = getmntopts(alp->mounta.opts, mopts_autofs, &alp->mounta.flags, 168 &alp->mounta.mntflags); 169 if (mp == NULL) 170 goto free_mem; 171 freemntopts(mp); 172 173 /* 174 * Is this a real autofs mount to be done now, rather than a 175 * trigger for a submount (me->map_modified) or a placeholder 176 * for subdirectories (me->map_faked)? 177 */ 178 if (!me->map_modified && !me->map_faked) { 179 /* 180 * Yes. Is it at level 0, i.e. is it supposed to 181 * be mounted atop the trigger vnode we're resolving? 182 */ 183 if (me->map_mntlevel == 0) { 184 /* 185 * Yes. Actually mount the map. 186 */ 187 struct autofs_args mnt_args; 188 189 mnt_args.version = AUTOFS_ARGSVERSION; 190 mnt_args.path = alp->mounta.path; 191 mnt_args.opts = alp->mounta.opts; 192 mnt_args.map = alp->mounta.map; 193 mnt_args.subdir = ""; /* this is a top-level (auto)mount of a map - no subdir */ 194 mnt_args.key = alp->mounta.key == NULL ? "" : alp->mounta.key; 195 mnt_args.mntflags = alp->mounta.mntflags; 196 mnt_args.direct = alp->mounta.isdirect; 197 /* 198 * This is a map that's being automounted on a trigger, 199 * rather than being mounted by automount. 200 */ 201 mnt_args.mount_type = MOUNT_TYPE_TRIGGERED_MAP; 202 if (alp->mounta.isdirect) 203 mnt_args.node_type = NT_TRIGGER; 204 else 205 mnt_args.node_type = 0; /* not a trigger */ 206 207 if (mount(MNTTYPE_AUTOFS, mntpnt, 208 alp->mounta.flags|MNT_AUTOMOUNTED|MNT_DONTBROWSE, 209 &mnt_args) == -1) 210 error = errno; 211 else { 212 error = get_triggered_mount_info(mntpnt, 213 mntpnt_fsid, fsidp, retflags); 214 } 215 216 /* 217 * This has already been processed; it doesn't belong 218 * on the list of future triggered mounts to be set up. 219 */ 220 free_action_list_fields(alp); 221 free(alp); 222 *alpp = NULL; 223 return (error); 224 } else { 225 /* 226 * No. We need to request a subtrigger to 227 * be planted to mount the map. 228 */ 229 if (!alp->mounta.isdirect) { 230 /* 231 * When we resolve the subtrigger, we 232 * should look in the map in which 233 * we found this entry, *not* in 234 * the map that's going to be mounted. 235 */ 236 free(alp->mounta.map); 237 if ((alp->mounta.map = strdup(mapname)) == NULL) 238 goto free_mem; 239 240 /* 241 * And the key should be the key used to 242 * look up this entry. 243 */ 244 if ((alp->mounta.key = strdup(key)) == NULL) 245 goto free_mem; 246 } 247 } 248 } 249 250 /* 251 * This is a subtrigger to be planted. For autofs and NFS 252 * mounts, we want the mount to be done directly atop the 253 * subtrigger, with no autofs mount involved; for other 254 * file systems, we want an autofs mount stuck in between 255 * them. 256 */ 257 if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0 || 258 strcmp(me->map_fstype, MNTTYPE_AUTOFS) == 0) 259 alp->mounta.needs_subtrigger = 0; 260 else 261 alp->mounta.needs_subtrigger = 1; 262 *alpp = alp; 263 return (0); 264 265free_mem: 266 /* 267 * We got an error, free the memory we allocated. 268 */ 269 syslog(LOG_ERR, "mount_autofs: memory allocation failure"); 270 free_action_list_fields(alp); 271 free(alp); 272 273 /* 274 * Return no action list entry. 275 */ 276 *alpp = NULL; 277 278 return (ENOMEM); 279} 280 281/* 282 * Set *directp to 1 if "direct" is found, and 0 otherwise 283 * (mounts are indirect by default). If both "direct" and "indirect" are 284 * found, the last one wins. 285 */ 286static int 287process_opts(char *options, uint32_t *directp) 288{ 289 char *opt, *opts, *lasts; 290 char buf[AUTOFS_MAXOPTSLEN]; 291 292 *directp = 0; 293 294 if (CHECK_STRCPY(buf, options, sizeof buf)) { 295 return -1; 296 } 297 298 opts = buf; 299 options[0] = '\0'; 300 301 while ((opt = strtok_r(opts, ",", &lasts)) != NULL) { 302 opts = NULL; 303 while (isspace(*opt)) { 304 opt++; 305 } 306 if (strcmp(opt, "direct") == 0) { 307 *directp = 1; 308 } else if (strcmp(opt, "indirect") == 0) { 309 *directp = 0; 310 } else if (strcmp(opt, "ignore") != 0) { 311 if (options[0] != '\0') { 312 if (CHECK_STRCAT( 313 options, ",", AUTOFS_MAXOPTSLEN)) { 314 return -1; 315 } 316 } 317 if (CHECK_STRCAT(options, opt, AUTOFS_MAXOPTSLEN)) { 318 return -1; 319 } 320 } 321 }; 322 return (0); 323} 324 325/* 326 * free items pointed to by members of an action list structure 327 */ 328void 329free_action_list_fields(action_list *alp) 330{ 331 if (alp == NULL) 332 return; 333 if (alp->mounta.dir) 334 free(alp->mounta.dir); 335 if (alp->mounta.opts) 336 free(alp->mounta.opts); 337 if (alp->mounta.path) 338 free(alp->mounta.path); 339 if (alp->mounta.map) 340 free(alp->mounta.map); 341 if (alp->mounta.subdir) 342 free(alp->mounta.subdir); 343 if (alp->mounta.trig_mntpnt) 344 free(alp->mounta.trig_mntpnt); 345 if (alp->mounta.key) 346 free(alp->mounta.key); 347} 348