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