xref: /dragonfly/sys/vfs/autofs/autofs_vnops.c (revision 65cc0652)
1 /*-
2  * Copyright (c) 2016 The DragonFly Project
3  * Copyright (c) 2014 The FreeBSD Foundation
4  * All rights reserved.
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/kernel.h>
33 #include <sys/module.h>
34 #include <sys/stat.h>
35 #include <sys/dirent.h>
36 #include <sys/namei.h>
37 #include <sys/nlookup.h>
38 #include <sys/mountctl.h>
39 
40 #include "autofs.h"
41 
42 static int	autofs_trigger_vn(struct vnode *vp, const char *path,
43 		    int pathlen, struct vnode **newvp);
44 
45 extern struct autofs_softc	*autofs_softc;
46 
47 static __inline size_t
48 autofs_dirent_reclen(const char *name)
49 {
50 	return (_DIRENT_RECLEN(strlen(name)));
51 }
52 
53 static int
54 test_fs_root(struct vnode *vp)
55 {
56 	int error;
57 
58 	if ((error = vget(vp, LK_SHARED)) != 0) {
59 		AUTOFS_WARN("vget failed with error %d", error);
60 		return (1);
61 	}
62 
63 	if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) {
64 		vput(vp);
65 		return (1);
66 	}
67 
68 	return (0);
69 }
70 
71 static int
72 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp)
73 {
74 	struct vnode *vp;
75 	struct nlookupdata nd;
76 	char *path;
77 	int error;
78 
79 	path = autofs_path(anp);
80 
81 	error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
82 	if (error == 0) {
83 		error = nlookup(&nd);
84 		if (error == 0) {
85 			vp = nd.nl_nch.ncp->nc_vp;
86 			error = test_fs_root(vp);
87 			if (error == 0)
88 				*vpp = vp;
89 		}
90 	}
91 	nlookup_done(&nd);
92 	kfree(path, M_AUTOFS);
93 
94 	return (error);
95 }
96 
97 static int
98 autofs_access(struct vop_access_args *ap)
99 {
100 	/*
101 	 * Nothing to do here; the only kind of access control
102 	 * needed is in autofs_mkdir().
103 	 */
104 	return (0);
105 }
106 
107 static int
108 autofs_getattr(struct vop_getattr_args *ap)
109 {
110 	struct vnode *vp = ap->a_vp;
111 	struct vattr *vap = ap->a_vap;
112 	struct autofs_node *anp = VTOI(vp);
113 	static bool warned = false;
114 
115 	KASSERT(vp->v_type == VDIR, ("!VDIR"));
116 
117 	/*
118 	 * The reason we must do this is that some tree-walking software,
119 	 * namely fts(3), assumes that stat(".") results will not change
120 	 * between chdir("subdir") and chdir(".."), and fails with ENOENT
121 	 * otherwise.
122 	 *
123 	 * XXX: Not supported on DragonFly.
124 	 * With the current trigger mechanism on DragonFly, the process
125 	 * will hang while in nlookup() in nlookup_fs_root().
126 	 */
127 	if (autofs_mount_on_stat) {
128 		if (!warned) {
129 			AUTOFS_WARN("vfs.autofs.mount_on_stat not supported");
130 			warned = true;
131 		}
132 	}
133 
134 	vap->va_type = VDIR;
135 	vap->va_mode = 0755;
136 	vap->va_nlink = 3; /* XXX: FreeBSD had it like this */
137 	vap->va_uid = 0;
138 	vap->va_gid = 0;
139 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
140 	vap->va_fileid = anp->an_ino;
141 	vap->va_size = S_BLKSIZE;
142 	vap->va_blocksize = S_BLKSIZE;
143 	vap->va_mtime = anp->an_ctime;
144 	vap->va_atime = anp->an_ctime;
145 	vap->va_ctime = anp->an_ctime;
146 	vap->va_gen = 0;
147 	vap->va_flags = 0;
148 	vap->va_rmajor = 0;
149 	vap->va_rminor = 0;
150 	vap->va_bytes = S_BLKSIZE;
151 	vap->va_filerev = 0;
152 	vap->va_spare = 0;
153 
154 	return (0);
155 }
156 
157 static int
158 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
159     struct vnode **newvp)
160 {
161 	struct autofs_node *anp = VTOI(vp);
162 	struct vnode *nvp = NULL;
163 	int error;
164 
165 	KKASSERT(!vn_islocked(vp));
166 
167 	if (test_fs_root(vp) == 0)
168 		goto mounted;
169 
170 	/*
171 	 * Don't remove this.  Without having this extra nlookup,
172 	 * automountd tries to mount the target filesystem twice
173 	 * and the second attempt to mount returns an error.
174 	 */
175 	if (nlookup_fs_root(anp, &nvp) == 0)
176 		goto mounted;
177 
178 	lockmgr(&autofs_softc->sc_lock, LK_EXCLUSIVE);
179 	error = autofs_trigger(anp, path, pathlen);
180 	lockmgr(&autofs_softc->sc_lock, LK_RELEASE);
181 
182 	if (error)
183 		return (error);
184 
185 	if (nlookup_fs_root(anp, &nvp))
186 		return (0);
187 
188 	/*
189 	 * If the operation that succeeded was mount, then mark
190 	 * the node as non-cached.  Otherwise, if someone unmounts
191 	 * the filesystem before the cache times out, we will fail
192 	 * to trigger.
193 	 */
194 	autofs_node_uncache(anp);
195 mounted:
196 	*newvp = nvp;
197 	KKASSERT(vn_islocked(*newvp));
198 
199 	return (0);
200 }
201 
202 static int
203 autofs_nresolve(struct vop_nresolve_args *ap)
204 {
205 	struct vnode *vp = NULL;
206 	struct vnode *dvp = ap->a_dvp;
207 	struct nchandle *nch = ap->a_nch;
208 	struct namecache *ncp = nch->ncp;
209 	struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
210 	struct autofs_node *anp = VTOI(dvp);
211 	struct autofs_node *child = NULL;
212 	int error;
213 
214 	if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false &&
215 	    autofs_ignore_thread() == false) {
216 		struct vnode *newvp = NULL;
217 
218 		cache_hold(nch);
219 		cache_unlock(nch);
220 		error = autofs_trigger_vn(dvp,
221 		    ncp->nc_name, ncp->nc_nlen, &newvp);
222 		cache_lock(nch);
223 		cache_drop(nch);
224 
225 		if (error)
226 			return (error);
227 		if (newvp != NULL) {
228 			KKASSERT(newvp->v_tag != VT_AUTOFS);
229 			vput(newvp);
230 			return (ESTALE);
231 		}
232 		return (0);
233 	}
234 
235 	lockmgr(&amp->am_lock, LK_SHARED);
236 	error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
237 	lockmgr(&amp->am_lock, LK_RELEASE);
238 
239 	if (error) {
240 		cache_setvp(nch, NULL);
241 		return (0);
242 	}
243 
244 	error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
245 	if (error == 0) {
246 		KKASSERT(vn_islocked(vp));
247 		vn_unlock(vp);
248 		cache_setvp(nch, vp);
249 		vrele(vp);
250 		return (0);
251 	}
252 
253 	return (error);
254 }
255 
256 static int
257 autofs_nmkdir(struct vop_nmkdir_args *ap)
258 {
259 	struct vnode *vp = NULL;
260 	struct vnode *dvp = ap->a_dvp;
261 	struct nchandle *nch = ap->a_nch;
262 	struct namecache *ncp = nch->ncp;
263 	struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
264 	struct autofs_node *anp = VTOI(dvp);
265 	struct autofs_node *child = NULL;
266 	int error;
267 
268 	/*
269 	 * Do not allow mkdir() if the calling thread is not
270 	 * automountd(8) descendant.
271 	 */
272 	if (autofs_ignore_thread() == false)
273 		return (EPERM);
274 
275 	lockmgr(&amp->am_lock, LK_EXCLUSIVE);
276 	error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
277 	lockmgr(&amp->am_lock, LK_RELEASE);
278 	KKASSERT(error == 0);
279 
280 	error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
281 	if (error == 0) {
282 		KKASSERT(vn_islocked(vp));
283 		cache_setunresolved(nch);
284 		cache_setvp(nch, vp);
285 		*(ap->a_vpp) = vp;
286 		return (0);
287 	}
288 
289 	return (error);
290 }
291 
292 static int
293 autofs_readdir_one(struct uio *uio, const char *name, ino_t ino,
294     size_t *reclenp)
295 {
296 	int error = 0;
297 
298 	if (reclenp != NULL)
299 		*reclenp = autofs_dirent_reclen(name);
300 
301 	if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name))
302 		return (EINVAL);
303 
304 	return (error);
305 }
306 
307 static int
308 autofs_readdir(struct vop_readdir_args *ap)
309 {
310 	struct vnode *vp = ap->a_vp;
311 	struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount);
312 	struct autofs_node *anp = VTOI(vp);
313 	struct autofs_node *child;
314 	struct uio *uio = ap->a_uio;
315 	ssize_t initial_resid = ap->a_uio->uio_resid;
316 	size_t reclen, reclens;
317 	int error;
318 
319 	KASSERT(vp->v_type == VDIR, ("!VDIR"));
320 
321 	if (autofs_cached(anp, NULL, 0) == false &&
322 	    autofs_ignore_thread() == false) {
323 		struct vnode *newvp = NULL;
324 		error = autofs_trigger_vn(vp, "", 0, &newvp);
325 		if (error)
326 			return (error);
327 		if (newvp != NULL) {
328 			KKASSERT(newvp->v_tag != VT_AUTOFS);
329 			vn_unlock(newvp);
330 			error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
331 			    ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
332 			vrele(newvp);
333 			return (error);
334 		}
335 		/* FALLTHROUGH */
336 	}
337 
338 	if (uio->uio_offset < 0)
339 		return (EINVAL);
340 
341 	if (ap->a_eofflag != NULL)
342 		*ap->a_eofflag = FALSE;
343 
344 	/*
345 	 * Write out the directory entry for ".".
346 	 */
347 	if (uio->uio_offset == 0) {
348 		error = autofs_readdir_one(uio, ".", anp->an_ino, &reclen);
349 		if (error)
350 			goto out;
351 	}
352 	reclens = autofs_dirent_reclen(".");
353 
354 	/*
355 	 * Write out the directory entry for "..".
356 	 */
357 	if (uio->uio_offset <= reclens) {
358 		if (uio->uio_offset != reclens)
359 			return (EINVAL);
360 		error = autofs_readdir_one(uio, "..",
361 		    (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino),
362 		    &reclen);
363 		if (error)
364 			goto out;
365 	}
366 	reclens += autofs_dirent_reclen("..");
367 
368 	/*
369 	 * Write out the directory entries for subdirectories.
370 	 */
371 	lockmgr(&amp->am_lock, LK_SHARED);
372 	RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
373 		/*
374 		 * Check the offset to skip entries returned by previous
375 		 * calls to getdents().
376 		 */
377 		if (uio->uio_offset > reclens) {
378 			reclens += autofs_dirent_reclen(child->an_name);
379 			continue;
380 		}
381 
382 		/*
383 		 * Prevent seeking into the middle of dirent.
384 		 */
385 		if (uio->uio_offset != reclens) {
386 			lockmgr(&amp->am_lock, LK_RELEASE);
387 			return (EINVAL);
388 		}
389 
390 		error = autofs_readdir_one(uio, child->an_name,
391 		    child->an_ino, &reclen);
392 		reclens += reclen;
393 		if (error) {
394 			lockmgr(&amp->am_lock, LK_RELEASE);
395 			goto out;
396 		}
397 	}
398 	lockmgr(&amp->am_lock, LK_RELEASE);
399 
400 	if (ap->a_eofflag != NULL)
401 		*ap->a_eofflag = TRUE;
402 
403 	return (0);
404 out:
405 	/*
406 	 * Return error if the initial buffer was too small to do anything.
407 	 */
408 	if (uio->uio_resid == initial_resid)
409 		return (error);
410 
411 	/*
412 	 * Don't return an error if we managed to copy out some entries.
413 	 */
414 	if (uio->uio_resid < reclen)
415 		return (0);
416 
417 	return (error);
418 }
419 
420 static int
421 autofs_reclaim(struct vop_reclaim_args *ap)
422 {
423 	struct vnode *vp = ap->a_vp;
424 	struct autofs_node *anp = VTOI(vp);
425 
426 	/*
427 	 * We do not free autofs_node here; instead we are
428 	 * destroying them in autofs_node_delete().
429 	 */
430 	lockmgr(&anp->an_vnode_lock, LK_EXCLUSIVE);
431 	anp->an_vnode = NULL;
432 	vp->v_data = NULL;
433 	lockmgr(&anp->an_vnode_lock, LK_RELEASE);
434 
435 	return (0);
436 }
437 
438 static int
439 autofs_mountctl(struct vop_mountctl_args *ap)
440 {
441 	struct mount *mp;
442 #if 0
443 	struct autofs_mount *amp;
444 #endif
445 	int res;
446 
447 	mp = ap->a_head.a_ops->head.vv_mount;
448 	lwkt_gettoken(&mp->mnt_token);
449 
450 	switch (ap->a_op) {
451 #if 0
452 	case MOUNTCTL_SET_EXPORT:
453 		amp = (struct autofs_mount*)mp->mnt_data;
454 		if (ap->a_ctllen != sizeof(struct export_args))
455 			res = (EINVAL);
456 		else
457 			res = vfs_export(mp, &amp->am_export,
458 			    (const struct export_args*)ap->a_ctl);
459 		break;
460 #endif
461 	default:
462 		res = vop_stdmountctl(ap);
463 		break;
464 	}
465 
466 	lwkt_reltoken(&mp->mnt_token);
467 	return (res);
468 }
469 
470 static int
471 autofs_print(struct vop_print_args *ap)
472 {
473 	struct autofs_node *anp = VTOI(ap->a_vp);
474 
475 	kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
476 	    anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached, anp->an_retries, anp->an_wildcards);
477 
478 	return (0);
479 }
480 
481 struct vop_ops autofs_vnode_vops = {
482 	.vop_default =		vop_defaultop,
483 	.vop_getpages =		vop_stdgetpages,
484 	.vop_putpages =		vop_stdputpages,
485 	.vop_access =		autofs_access,
486 	.vop_getattr =		autofs_getattr,
487 	.vop_nresolve =		autofs_nresolve,
488 	.vop_nmkdir =		autofs_nmkdir,
489 	.vop_readdir =		autofs_readdir,
490 	.vop_reclaim =		autofs_reclaim,
491 	.vop_mountctl =		autofs_mountctl,
492 	.vop_print =		autofs_print,
493 #if 0
494 	.vop_nlookupdotdot =	NULL,
495 #endif
496 };
497 
498 int
499 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
500     const char *name, int namelen, struct autofs_node **anpp)
501 {
502 	struct autofs_node *anp;
503 
504 	AUTOFS_ASSERT_XLOCKED(amp);
505 
506 	if (parent != NULL) {
507 		AUTOFS_ASSERT_XLOCKED(parent->an_mount);
508 		KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT,
509 		    ("node \"%s\" already exists", name));
510 	}
511 
512 	anp = objcache_get(autofs_node_objcache, M_WAITOK);
513 	if (namelen >= 0)
514 		anp->an_name = kstrndup(name, namelen, M_AUTOFS);
515 	else
516 		anp->an_name = kstrdup(name, M_AUTOFS);
517 	anp->an_ino = amp->am_last_ino++;
518 	callout_init(&anp->an_callout);
519 	lockinit(&anp->an_vnode_lock, "autofsvnlk", 0, 0);
520 	getnanotime(&anp->an_ctime);
521 	anp->an_parent = parent;
522 	anp->an_mount = amp;
523 	anp->an_vnode = NULL;
524 	anp->an_cached = false;
525 	anp->an_wildcards = false;
526 	anp->an_retries = 0;
527 	if (parent != NULL)
528 		RB_INSERT(autofs_node_tree, &parent->an_children, anp);
529 	RB_INIT(&anp->an_children);
530 
531 	*anpp = anp;
532 
533 	return (0);
534 }
535 
536 int
537 autofs_node_find(struct autofs_node *parent, const char *name,
538     int namelen, struct autofs_node **anpp)
539 {
540 	struct autofs_node *anp, find;
541 	int error;
542 
543 	AUTOFS_ASSERT_LOCKED(parent->an_mount);
544 
545 	if (namelen >= 0)
546 		find.an_name = kstrndup(name, namelen, M_AUTOFS);
547 	else
548 		find.an_name = kstrdup(name, M_AUTOFS);
549 
550 	anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
551 	if (anp != NULL) {
552 		error = 0;
553 		if (anpp != NULL)
554 			*anpp = anp;
555 	} else {
556 		error = ENOENT;
557 	}
558 
559 	kfree(find.an_name, M_AUTOFS);
560 
561 	return (error);
562 }
563 
564 void
565 autofs_node_delete(struct autofs_node *anp)
566 {
567 	AUTOFS_ASSERT_XLOCKED(anp->an_mount);
568 	KASSERT(RB_EMPTY(&anp->an_children), ("have children"));
569 
570 	callout_drain(&anp->an_callout);
571 
572 	if (anp->an_parent != NULL)
573 		RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp);
574 
575 	lockuninit(&anp->an_vnode_lock);
576 	kfree(anp->an_name, M_AUTOFS);
577 	objcache_put(autofs_node_objcache, anp);
578 }
579 
580 int
581 autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags,
582     struct vnode **vpp)
583 {
584 	struct vnode *vp = NULL;
585 	int error;
586 retry:
587 	AUTOFS_ASSERT_UNLOCKED(anp->an_mount);
588 	lockmgr(&anp->an_vnode_lock, LK_EXCLUSIVE);
589 
590 	vp = anp->an_vnode;
591 	if (vp != NULL) {
592 		vhold(vp);
593 		lockmgr(&anp->an_vnode_lock, LK_RELEASE);
594 
595 		error = vget(vp, flags | LK_RETRY);
596 		if (error) {
597 			AUTOFS_WARN("vget failed with error %d", error);
598 			vdrop(vp);
599 			goto retry;
600 		}
601 		vdrop(vp);
602 		*vpp = vp;
603 		return (0);
604 	}
605 
606 	lockmgr(&anp->an_vnode_lock, LK_RELEASE);
607 
608 	error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
609 	if (error)
610 		return (error);
611 	vp->v_type = VDIR;
612 	vp->v_data = anp;
613 
614 	KASSERT(anp->an_vnode == NULL, ("lost race"));
615 	anp->an_vnode = vp;
616 	*vpp = vp;
617 
618 	return (0);
619 }
620