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