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