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