xref: /dragonfly/sys/vfs/autofs/autofs_vnops.c (revision e62ef63c)
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 	mtx_lock_sh_quick(&amp->am_lock);
236 	error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
237 	mtx_unlock_sh(&amp->am_lock);
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 	mtx_lock_ex_quick(&amp->am_lock);
276 	error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
277 	mtx_unlock_ex(&amp->am_lock);
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 	mtx_lock_sh_quick(&amp->am_lock);
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 			mtx_unlock_sh(&amp->am_lock);
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 			mtx_unlock_sh(&amp->am_lock);
395 			goto out;
396 		}
397 	}
398 	mtx_unlock_sh(&amp->am_lock);
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 	mtx_lock_ex_quick(&anp->an_vnode_lock);
431 	anp->an_vnode = NULL;
432 	vp->v_data = NULL;
433 	mtx_unlock_ex(&anp->an_vnode_lock);
434 
435 	return (0);
436 }
437 
438 static int
439 autofs_mountctl(struct vop_mountctl_args *ap)
440 {
441 	struct mount *mp;
442 	int res;
443 
444 	mp = ap->a_head.a_ops->head.vv_mount;
445 	lwkt_gettoken(&mp->mnt_token);
446 
447 	switch (ap->a_op) {
448 	//case ...:
449 	//	break;
450 	default:
451 		res = vop_stdmountctl(ap);
452 		break;
453 	}
454 
455 	lwkt_reltoken(&mp->mnt_token);
456 	return (res);
457 }
458 
459 static int
460 autofs_print(struct vop_print_args *ap)
461 {
462 	struct autofs_node *anp = VTOI(ap->a_vp);
463 
464 	kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
465 	    anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached,
466 	    anp->an_retries, anp->an_wildcards);
467 
468 	return (0);
469 }
470 
471 struct vop_ops autofs_vnode_vops = {
472 	.vop_default =		vop_defaultop,
473 	.vop_getpages =		vop_stdgetpages,
474 	.vop_putpages =		vop_stdputpages,
475 	.vop_access =		autofs_access,
476 	.vop_getattr =		autofs_getattr,
477 	.vop_nresolve =		autofs_nresolve,
478 	.vop_nmkdir =		autofs_nmkdir,
479 	.vop_readdir =		autofs_readdir,
480 	.vop_reclaim =		autofs_reclaim,
481 	.vop_mountctl =		autofs_mountctl,
482 	.vop_print =		autofs_print,
483 };
484 
485 int
486 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
487     const char *name, int namelen, struct autofs_node **anpp)
488 {
489 	struct autofs_node *anp;
490 
491 	KKASSERT(mtx_islocked_ex(&amp->am_lock));
492 
493 	if (parent != NULL) {
494 		KKASSERT(mtx_islocked_ex(&parent->an_mount->am_lock));
495 		KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT,
496 		    ("node \"%s\" already exists", name));
497 	}
498 
499 	anp = objcache_get(autofs_node_objcache, M_WAITOK);
500 	if (namelen >= 0)
501 		anp->an_name = kstrndup(name, namelen, M_AUTOFS);
502 	else
503 		anp->an_name = kstrdup(name, M_AUTOFS);
504 	anp->an_ino = amp->am_last_ino++;
505 	callout_init(&anp->an_callout);
506 	mtx_init(&anp->an_vnode_lock, "autofsvnlk");
507 	getnanotime(&anp->an_ctime);
508 	anp->an_parent = parent;
509 	anp->an_mount = amp;
510 	anp->an_vnode = NULL;
511 	anp->an_cached = false;
512 	anp->an_wildcards = false;
513 	anp->an_retries = 0;
514 	if (parent != NULL)
515 		RB_INSERT(autofs_node_tree, &parent->an_children, anp);
516 	RB_INIT(&anp->an_children);
517 
518 	*anpp = anp;
519 
520 	return (0);
521 }
522 
523 int
524 autofs_node_find(struct autofs_node *parent, const char *name,
525     int namelen, struct autofs_node **anpp)
526 {
527 	struct autofs_node *anp, find;
528 	int error;
529 
530 	KKASSERT(mtx_islocked(&parent->an_mount->am_lock));
531 
532 	if (namelen >= 0)
533 		find.an_name = kstrndup(name, namelen, M_AUTOFS);
534 	else
535 		find.an_name = kstrdup(name, M_AUTOFS);
536 
537 	anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
538 	if (anp != NULL) {
539 		error = 0;
540 		if (anpp != NULL)
541 			*anpp = anp;
542 	} else {
543 		error = ENOENT;
544 	}
545 
546 	kfree(find.an_name, M_AUTOFS);
547 
548 	return (error);
549 }
550 
551 void
552 autofs_node_delete(struct autofs_node *anp)
553 {
554 	KKASSERT(mtx_islocked_ex(&anp->an_mount->am_lock));
555 	KASSERT(RB_EMPTY(&anp->an_children), ("have children"));
556 
557 	callout_drain(&anp->an_callout);
558 
559 	if (anp->an_parent != NULL)
560 		RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp);
561 
562 	mtx_uninit(&anp->an_vnode_lock);
563 	kfree(anp->an_name, M_AUTOFS);
564 	objcache_put(autofs_node_objcache, anp);
565 }
566 
567 int
568 autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags,
569     struct vnode **vpp)
570 {
571 	struct vnode *vp = NULL;
572 	int error;
573 retry:
574 	KKASSERT(mtx_notlocked(&anp->an_mount->am_lock));
575 	mtx_lock_ex_quick(&anp->an_vnode_lock);
576 
577 	vp = anp->an_vnode;
578 	if (vp != NULL) {
579 		vhold(vp);
580 		mtx_unlock_ex(&anp->an_vnode_lock);
581 
582 		error = vget(vp, flags | LK_RETRY);
583 		if (error) {
584 			AUTOFS_WARN("vget failed with error %d", error);
585 			vdrop(vp);
586 			goto retry;
587 		}
588 		vdrop(vp);
589 		*vpp = vp;
590 		return (0);
591 	}
592 
593 	mtx_unlock_ex(&anp->an_vnode_lock);
594 
595 	error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
596 	if (error)
597 		return (error);
598 	vp->v_type = VDIR;
599 	vp->v_data = anp;
600 
601 	KASSERT(anp->an_vnode == NULL, ("lost race"));
602 	anp->an_vnode = vp;
603 	*vpp = vp;
604 
605 	return (0);
606 }
607