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