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