1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2014 The FreeBSD Foundation
5 *
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/condvar.h>
36#include <sys/dirent.h>
37#include <sys/fcntl.h>
38#include <sys/lock.h>
39#include <sys/mount.h>
40#include <sys/mutex.h>
41#include <sys/namei.h>
42#include <sys/signalvar.h>
43#include <sys/stat.h>
44#include <sys/taskqueue.h>
45#include <sys/tree.h>
46#include <sys/vnode.h>
47#include <machine/atomic.h>
48#include <vm/uma.h>
49
50#include <fs/autofs/autofs.h>
51
52static int	autofs_trigger_vn(struct vnode *vp, const char *path,
53		    int pathlen, struct vnode **newvp);
54
55extern struct autofs_softc	*autofs_softc;
56
57static int
58autofs_access(struct vop_access_args *ap)
59{
60
61	/*
62	 * Nothing to do here; the only kind of access control
63	 * needed is in autofs_mkdir().
64	 */
65
66	return (0);
67}
68
69static int
70autofs_getattr(struct vop_getattr_args *ap)
71{
72	struct vnode *vp, *newvp;
73	struct autofs_node *anp;
74	struct mount *mp;
75	struct vattr *vap;
76	int error;
77
78	vp = ap->a_vp;
79	anp = vp->v_data;
80	mp = vp->v_mount;
81	vap = ap->a_vap;
82
83	KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR"));
84
85	/*
86	 * The reason we must do this is that some tree-walking software,
87	 * namely fts(3), assumes that stat(".") results will not change
88	 * between chdir("subdir") and chdir(".."), and fails with ENOENT
89	 * otherwise.
90	 */
91	if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false &&
92	    autofs_ignore_thread(curthread) == false) {
93		error = autofs_trigger_vn(vp, "", 0, &newvp);
94		if (error != 0)
95			return (error);
96
97		if (newvp != NULL) {
98			error = VOP_GETATTR(newvp, ap->a_vap,
99			    ap->a_cred);
100			vput(newvp);
101			return (error);
102		}
103	}
104
105	vap->va_type = VDIR;
106	vap->va_mode = 0755;
107	vap->va_nlink = 3; /* XXX */
108	vap->va_uid = 0;
109	vap->va_gid = 0;
110	vap->va_rdev = NODEV;
111	vap->va_fsid = mp->mnt_stat.f_fsid.val[0];
112	vap->va_fileid = anp->an_fileno;
113	vap->va_size = S_BLKSIZE;
114	vap->va_blocksize = S_BLKSIZE;
115	vap->va_mtime = anp->an_ctime;
116	vap->va_atime = anp->an_ctime;
117	vap->va_ctime = anp->an_ctime;
118	vap->va_birthtime = anp->an_ctime;
119	vap->va_gen = 0;
120	vap->va_flags = 0;
121	vap->va_rdev = 0;
122	vap->va_bytes = S_BLKSIZE;
123	vap->va_filerev = 0;
124	vap->va_spare = 0;
125
126	return (0);
127}
128
129/*
130 * Unlock the vnode, request automountd(8) action, and then lock it back.
131 * If anything got mounted on top of the vnode, return the new filesystem's
132 * root vnode in 'newvp', locked.
133 */
134static int
135autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
136    struct vnode **newvp)
137{
138	struct autofs_node *anp;
139	int error, lock_flags;
140
141	anp = vp->v_data;
142
143	/*
144	 * Release the vnode lock, so that other operations, in partcular
145	 * mounting a filesystem on top of it, can proceed.  Increase use
146	 * count, to prevent the vnode from being deallocated and to prevent
147	 * filesystem from being unmounted.
148	 */
149	lock_flags = VOP_ISLOCKED(vp);
150	vref(vp);
151	VOP_UNLOCK(vp);
152
153	sx_xlock(&autofs_softc->sc_lock);
154
155	/*
156	 * XXX: Workaround for mounting the same thing multiple times; revisit.
157	 */
158	if (vp->v_mountedhere != NULL) {
159		error = 0;
160		goto mounted;
161	}
162
163	error = autofs_trigger(anp, path, pathlen);
164mounted:
165	sx_xunlock(&autofs_softc->sc_lock);
166	vn_lock(vp, lock_flags | LK_RETRY);
167	vunref(vp);
168	if (VN_IS_DOOMED(vp)) {
169		AUTOFS_DEBUG("VIRF_DOOMED");
170		return (ENOENT);
171	}
172
173	if (error != 0)
174		return (error);
175
176	if (vp->v_mountedhere == NULL) {
177		*newvp = NULL;
178		return (0);
179	} else {
180		/*
181		 * If the operation that succeeded was mount, then mark
182		 * the node as non-cached.  Otherwise, if someone unmounts
183		 * the filesystem before the cache times out, we will fail
184		 * to trigger.
185		 */
186		anp->an_cached = false;
187	}
188
189	error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp);
190	if (error != 0) {
191		AUTOFS_WARN("VFS_ROOT() failed with error %d", error);
192		return (error);
193	}
194
195	return (0);
196}
197
198static int
199autofs_vget_callback(struct mount *mp, void *arg, int flags,
200    struct vnode **vpp)
201{
202
203	return (autofs_node_vn(arg, mp, flags, vpp));
204}
205
206static int
207autofs_lookup(struct vop_lookup_args *ap)
208{
209	struct vnode *dvp, *newvp, **vpp;
210	struct mount *mp;
211	struct autofs_mount *amp;
212	struct autofs_node *anp, *child;
213	struct componentname *cnp;
214	int error;
215
216	dvp = ap->a_dvp;
217	vpp = ap->a_vpp;
218	mp = dvp->v_mount;
219	amp = VFSTOAUTOFS(mp);
220	anp = dvp->v_data;
221	cnp = ap->a_cnp;
222
223	if (cnp->cn_flags & ISDOTDOT) {
224		KASSERT(anp->an_parent != NULL, ("NULL parent"));
225		/*
226		 * Note that in this case, dvp is the child vnode, and we
227		 * are looking up the parent vnode - exactly reverse from
228		 * normal operation.  Unlocking dvp requires some rather
229		 * tricky unlock/relock dance to prevent mp from being freed;
230		 * use vn_vget_ino_gen() which takes care of all that.
231		 */
232		error = vn_vget_ino_gen(dvp, autofs_vget_callback,
233		    anp->an_parent, cnp->cn_lkflags, vpp);
234		if (error != 0) {
235			AUTOFS_WARN("vn_vget_ino_gen() failed with error %d",
236			    error);
237			return (error);
238		}
239		return (error);
240	}
241
242	if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
243		vref(dvp);
244		*vpp = dvp;
245
246		return (0);
247	}
248
249	if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false &&
250	    autofs_ignore_thread(curthread) == false) {
251		error = autofs_trigger_vn(dvp,
252		    cnp->cn_nameptr, cnp->cn_namelen, &newvp);
253		if (error != 0)
254			return (error);
255
256		if (newvp != NULL) {
257			/*
258			 * The target filesystem got automounted.
259			 * Let the lookup(9) go around with the same
260			 * path component.
261			 */
262			vput(newvp);
263			return (ERELOOKUP);
264		}
265	}
266
267	AUTOFS_SLOCK(amp);
268	error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child);
269	if (error != 0) {
270		if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
271			AUTOFS_SUNLOCK(amp);
272			return (EJUSTRETURN);
273		}
274
275		AUTOFS_SUNLOCK(amp);
276		return (ENOENT);
277	}
278
279	/*
280	 * XXX: Dropping the node here is ok, because we never remove nodes.
281	 */
282	AUTOFS_SUNLOCK(amp);
283
284	error = autofs_node_vn(child, mp, cnp->cn_lkflags, vpp);
285	if (error != 0) {
286		if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE)
287			return (EJUSTRETURN);
288
289		return (error);
290	}
291
292	return (0);
293}
294
295static int
296autofs_mkdir(struct vop_mkdir_args *ap)
297{
298	struct vnode *vp;
299	struct autofs_node *anp;
300	struct autofs_mount *amp;
301	struct autofs_node *child;
302	int error;
303
304	vp = ap->a_dvp;
305	anp = vp->v_data;
306	amp = VFSTOAUTOFS(vp->v_mount);
307
308	/*
309	 * Do not allow mkdir() if the calling thread is not
310	 * automountd(8) descendant.
311	 */
312	if (autofs_ignore_thread(curthread) == false)
313		return (EPERM);
314
315	AUTOFS_XLOCK(amp);
316	error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr,
317	    ap->a_cnp->cn_namelen, &child);
318	if (error != 0) {
319		AUTOFS_XUNLOCK(amp);
320		return (error);
321	}
322	AUTOFS_XUNLOCK(amp);
323
324	error = autofs_node_vn(child, vp->v_mount, LK_EXCLUSIVE, ap->a_vpp);
325
326	return (error);
327}
328
329static int
330autofs_print(struct vop_print_args *ap)
331{
332	struct vnode *vp;
333	struct autofs_node *anp;
334
335	vp = ap->a_vp;
336	anp = vp->v_data;
337
338	printf("    name \"%s\", fileno %d, cached %d, wildcards %d\n",
339	    anp->an_name, anp->an_fileno, anp->an_cached, anp->an_wildcards);
340
341	return (0);
342}
343
344/*
345 * Write out a single 'struct dirent', based on 'name' and 'fileno' arguments.
346 */
347static int
348autofs_readdir_one(struct uio *uio, const char *name, int fileno,
349    size_t *reclenp)
350{
351	struct dirent dirent;
352	size_t namlen, reclen;
353	int error;
354
355	namlen = strlen(name);
356	reclen = _GENERIC_DIRLEN(namlen);
357	if (reclenp != NULL)
358		*reclenp = reclen;
359
360	if (uio == NULL)
361		return (0);
362
363	if (uio->uio_resid < reclen)
364		return (EINVAL);
365
366	dirent.d_fileno = fileno;
367	dirent.d_off = uio->uio_offset + reclen;
368	dirent.d_reclen = reclen;
369	dirent.d_type = DT_DIR;
370	dirent.d_namlen = namlen;
371	memcpy(dirent.d_name, name, namlen);
372	dirent_terminate(&dirent);
373	error = uiomove(&dirent, reclen, uio);
374
375	return (error);
376}
377
378static size_t
379autofs_dirent_reclen(const char *name)
380{
381	size_t reclen;
382
383	(void)autofs_readdir_one(NULL, name, -1, &reclen);
384
385	return (reclen);
386}
387
388static int
389autofs_readdir(struct vop_readdir_args *ap)
390{
391	struct vnode *vp, *newvp;
392	struct autofs_mount *amp;
393	struct autofs_node *anp, *child;
394	struct uio *uio;
395	size_t reclen, reclens;
396	ssize_t initial_resid;
397	int error;
398
399	vp = ap->a_vp;
400	amp = VFSTOAUTOFS(vp->v_mount);
401	anp = vp->v_data;
402	uio = ap->a_uio;
403	initial_resid = ap->a_uio->uio_resid;
404
405	KASSERT(vp->v_type == VDIR, ("!VDIR"));
406
407	if (autofs_cached(anp, NULL, 0) == false &&
408	    autofs_ignore_thread(curthread) == false) {
409		error = autofs_trigger_vn(vp, "", 0, &newvp);
410		if (error != 0)
411			return (error);
412
413		if (newvp != NULL) {
414			error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
415			    ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
416			vput(newvp);
417			return (error);
418		}
419	}
420
421	if (uio->uio_offset < 0)
422		return (EINVAL);
423
424	if (ap->a_eofflag != NULL)
425		*ap->a_eofflag = FALSE;
426
427	/*
428	 * Write out the directory entry for ".".  This is conditional
429	 * on the current offset into the directory; same applies to the
430	 * other two cases below.
431	 */
432	if (uio->uio_offset == 0) {
433		error = autofs_readdir_one(uio, ".", anp->an_fileno, &reclen);
434		if (error != 0)
435			goto out;
436	}
437	reclens = autofs_dirent_reclen(".");
438
439	/*
440	 * Write out the directory entry for "..".
441	 */
442	if (uio->uio_offset <= reclens) {
443		if (uio->uio_offset != reclens)
444			return (EINVAL);
445		if (anp->an_parent == NULL) {
446			error = autofs_readdir_one(uio, "..",
447			    anp->an_fileno, &reclen);
448		} else {
449			error = autofs_readdir_one(uio, "..",
450			    anp->an_parent->an_fileno, &reclen);
451		}
452		if (error != 0)
453			goto out;
454	}
455
456	reclens += autofs_dirent_reclen("..");
457
458	/*
459	 * Write out the directory entries for subdirectories.
460	 */
461	AUTOFS_SLOCK(amp);
462	RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
463		/*
464		 * Check the offset to skip entries returned by previous
465		 * calls to getdents().
466		 */
467		if (uio->uio_offset > reclens) {
468			reclens += autofs_dirent_reclen(child->an_name);
469			continue;
470		}
471
472		/*
473		 * Prevent seeking into the middle of dirent.
474		 */
475		if (uio->uio_offset != reclens) {
476			AUTOFS_SUNLOCK(amp);
477			return (EINVAL);
478		}
479
480		error = autofs_readdir_one(uio, child->an_name,
481		    child->an_fileno, &reclen);
482		reclens += reclen;
483		if (error != 0) {
484			AUTOFS_SUNLOCK(amp);
485			goto out;
486		}
487	}
488	AUTOFS_SUNLOCK(amp);
489
490	if (ap->a_eofflag != NULL)
491		*ap->a_eofflag = TRUE;
492
493	return (0);
494
495out:
496	/*
497	 * Return error if the initial buffer was too small to do anything.
498	 */
499	if (uio->uio_resid == initial_resid)
500		return (error);
501
502	/*
503	 * Don't return an error if we managed to copy out some entries.
504	 */
505	if (uio->uio_resid < reclen)
506		return (0);
507
508	return (error);
509}
510
511static int
512autofs_reclaim(struct vop_reclaim_args *ap)
513{
514	struct vnode *vp;
515	struct autofs_node *anp;
516
517	vp = ap->a_vp;
518	anp = vp->v_data;
519
520	/*
521	 * We do not free autofs_node here; instead we are
522	 * destroying them in autofs_node_delete().
523	 */
524	sx_xlock(&anp->an_vnode_lock);
525	anp->an_vnode = NULL;
526	vp->v_data = NULL;
527	sx_xunlock(&anp->an_vnode_lock);
528
529	return (0);
530}
531
532struct vop_vector autofs_vnodeops = {
533	.vop_default =		&default_vnodeops,
534
535	.vop_access =		autofs_access,
536	.vop_lookup =		autofs_lookup,
537	.vop_create =		VOP_EOPNOTSUPP,
538	.vop_getattr =		autofs_getattr,
539	.vop_link =		VOP_EOPNOTSUPP,
540	.vop_mkdir =		autofs_mkdir,
541	.vop_mknod =		VOP_EOPNOTSUPP,
542	.vop_print =		autofs_print,
543	.vop_read =		VOP_EOPNOTSUPP,
544	.vop_readdir =		autofs_readdir,
545	.vop_remove =		VOP_EOPNOTSUPP,
546	.vop_rename =		VOP_EOPNOTSUPP,
547	.vop_rmdir =		VOP_EOPNOTSUPP,
548	.vop_setattr =		VOP_EOPNOTSUPP,
549	.vop_symlink =		VOP_EOPNOTSUPP,
550	.vop_write =		VOP_EOPNOTSUPP,
551	.vop_reclaim =		autofs_reclaim,
552};
553VFS_VOP_VECTOR_REGISTER(autofs_vnodeops);
554
555int
556autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
557    const char *name, int namelen, struct autofs_node **anpp)
558{
559	struct autofs_node *anp;
560
561	if (parent != NULL) {
562		AUTOFS_ASSERT_XLOCKED(parent->an_mount);
563
564		KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT,
565		    ("node \"%s\" already exists", name));
566	}
567
568	anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO);
569	if (namelen >= 0)
570		anp->an_name = strndup(name, namelen, M_AUTOFS);
571	else
572		anp->an_name = strdup(name, M_AUTOFS);
573	anp->an_fileno = atomic_fetchadd_int(&amp->am_last_fileno, 1);
574	callout_init(&anp->an_callout, 1);
575	/*
576	 * The reason for SX_NOWITNESS here is that witness(4)
577	 * cannot tell vnodes apart, so the following perfectly
578	 * valid lock order...
579	 *
580	 * vnode lock A -> autofsvlk B -> vnode lock B
581	 *
582	 * ... gets reported as a LOR.
583	 */
584	sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS);
585	getnanotime(&anp->an_ctime);
586	anp->an_parent = parent;
587	anp->an_mount = amp;
588	if (parent != NULL)
589		RB_INSERT(autofs_node_tree, &parent->an_children, anp);
590	RB_INIT(&anp->an_children);
591
592	*anpp = anp;
593	return (0);
594}
595
596int
597autofs_node_find(struct autofs_node *parent, const char *name,
598    int namelen, struct autofs_node **anpp)
599{
600	struct autofs_node *anp, find;
601	int error;
602
603	AUTOFS_ASSERT_LOCKED(parent->an_mount);
604
605	if (namelen >= 0)
606		find.an_name = strndup(name, namelen, M_AUTOFS);
607	else
608		find.an_name = strdup(name, M_AUTOFS);
609
610	anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
611	if (anp != NULL) {
612		error = 0;
613		if (anpp != NULL)
614			*anpp = anp;
615	} else {
616		error = ENOENT;
617	}
618
619	free(find.an_name, M_AUTOFS);
620
621	return (error);
622}
623
624void
625autofs_node_delete(struct autofs_node *anp)
626{
627	struct autofs_node *parent;
628
629	AUTOFS_ASSERT_XLOCKED(anp->an_mount);
630	KASSERT(RB_EMPTY(&anp->an_children), ("have children"));
631
632	callout_drain(&anp->an_callout);
633
634	parent = anp->an_parent;
635	if (parent != NULL)
636		RB_REMOVE(autofs_node_tree, &parent->an_children, anp);
637	sx_destroy(&anp->an_vnode_lock);
638	free(anp->an_name, M_AUTOFS);
639	uma_zfree(autofs_node_zone, anp);
640}
641
642int
643autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags,
644    struct vnode **vpp)
645{
646	struct vnode *vp;
647	int error;
648
649	AUTOFS_ASSERT_UNLOCKED(anp->an_mount);
650
651	sx_xlock(&anp->an_vnode_lock);
652
653	vp = anp->an_vnode;
654	if (vp != NULL) {
655		error = vget(vp, flags | LK_RETRY);
656		if (error != 0) {
657			AUTOFS_WARN("vget failed with error %d", error);
658			sx_xunlock(&anp->an_vnode_lock);
659			return (error);
660		}
661		if (VN_IS_DOOMED(vp)) {
662			/*
663			 * We got forcibly unmounted.
664			 */
665			AUTOFS_DEBUG("doomed vnode");
666			sx_xunlock(&anp->an_vnode_lock);
667			vput(vp);
668
669			return (ENOENT);
670		}
671
672		*vpp = vp;
673		sx_xunlock(&anp->an_vnode_lock);
674		return (0);
675	}
676
677	error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp);
678	if (error != 0) {
679		sx_xunlock(&anp->an_vnode_lock);
680		return (error);
681	}
682
683	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
684
685	vp->v_type = VDIR;
686	if (anp->an_parent == NULL)
687		vp->v_vflag |= VV_ROOT;
688	vp->v_data = anp;
689
690	VN_LOCK_ASHARE(vp);
691
692	error = insmntque(vp, mp);
693	if (error != 0) {
694		AUTOFS_DEBUG("insmntque() failed with error %d", error);
695		sx_xunlock(&anp->an_vnode_lock);
696		return (error);
697	}
698
699	KASSERT(anp->an_vnode == NULL, ("lost race"));
700	anp->an_vnode = vp;
701
702	sx_xunlock(&anp->an_vnode_lock);
703
704	vn_set_state(vp, VSTATE_CONSTRUCTED);
705	*vpp = vp;
706	return (0);
707}
708