xref: /dragonfly/sys/vfs/autofs/autofs_vnops.c (revision e6d22e9b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2016 Tomohiro Kusumi <tkusumi@netbsd.org>
5  * Copyright (c) 2016 The DragonFly Project
6  * Copyright (c) 2014 The FreeBSD Foundation
7  * All rights reserved.
8  *
9  * This software was developed by Edward Tomasz Napierala under sponsorship
10  * from the FreeBSD Foundation.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  */
34 
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/stat.h>
38 #include <sys/dirent.h>
39 #include <sys/nlookup.h>
40 #include <sys/mountctl.h>
41 
42 #include "autofs.h"
43 
44 static bool
45 test_fs_root(struct vnode *vp)
46 {
47 	int error;
48 
49 	if ((error = vget(vp, LK_SHARED)) != 0) {
50 		AUTOFS_WARN("vget failed with error %d", error);
51 		return (false);
52 	}
53 
54 	if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) {
55 		vput(vp);
56 		return (false);
57 	}
58 
59 	return (true);
60 }
61 
62 static bool
63 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp)
64 {
65 	struct nlookupdata nd;
66 	char *path;
67 	int error;
68 
69 	path = autofs_path(anp);
70 
71 	error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
72 	if (error == 0) {
73 		error = nlookup(&nd);
74 		if (error == 0) {
75 			struct vnode *vp = nd.nl_nch.ncp->nc_vp;
76 			if (test_fs_root(vp) == true)
77 				*vpp = vp;
78 			else
79 				error = 1;
80 		}
81 	}
82 	nlookup_done(&nd);
83 	kfree(path, M_AUTOFS);
84 
85 	return (error ? false : true);
86 }
87 
88 static int
89 autofs_access(struct vop_access_args *ap)
90 {
91 	/*
92 	 * Nothing to do here; the only kind of access control
93 	 * needed is in autofs_mkdir().
94 	 */
95 	return (0);
96 }
97 
98 static int
99 autofs_getattr(struct vop_getattr_args *ap)
100 {
101 	struct vnode *vp = ap->a_vp;
102 	struct vattr *vap = ap->a_vap;
103 	struct autofs_node *anp = VTOI(vp);
104 
105 	KASSERT(vp->v_type == VDIR, ("!VDIR"));
106 
107 	/*
108 	 * The reason we must do this is that some tree-walking software,
109 	 * namely fts(3), assumes that stat(".") results will not change
110 	 * between chdir("subdir") and chdir(".."), and fails with ENOENT
111 	 * otherwise.
112 	 *
113 	 * XXX: Not supported on DragonFly.
114 	 * With the current trigger mechanism on DragonFly, the process
115 	 * will hang while in nlookup() in nlookup_fs_root().
116 	 */
117 #if 0
118 	if (autofs_mount_on_stat && ...) {
119 	}
120 #endif
121 	vap->va_type = VDIR;
122 	vap->va_nlink = 3; /* XXX: FreeBSD had it like this */
123 	vap->va_mode = 0755;
124 	vap->va_uid = 0;
125 	vap->va_gid = 0;
126 	vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
127 	vap->va_fileid = anp->an_ino;
128 	vap->va_size = S_BLKSIZE;
129 	vap->va_blocksize = S_BLKSIZE;
130 	vap->va_atime = anp->an_ctime;
131 	vap->va_mtime = anp->an_ctime;
132 	vap->va_ctime = anp->an_ctime;
133 	vap->va_gen = 0;
134 	vap->va_flags = 0;
135 	vap->va_rmajor = 0;
136 	vap->va_rminor = 0;
137 	vap->va_bytes = S_BLKSIZE;
138 	vap->va_filerev = 0;
139 	vap->va_vaflags = 0;
140 	vap->va_spare = 0;
141 
142 	return (0);
143 }
144 
145 static int
146 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
147     struct vnode **newvp)
148 {
149 	struct autofs_node *anp = VTOI(vp);
150 	struct vnode *nvp = NULL;
151 	int error;
152 
153 	KKASSERT(!vn_islocked(vp));
154 
155 	if (test_fs_root(vp) == true)
156 		goto mounted;
157 
158 	/*
159 	 * Don't remove this lookup.  Without this, the process
160 	 * may trigger automountd(8) to mount the filesystem for the
161 	 * second time after successful mount, and the second attempt
162 	 * will fail.
163 	 */
164 	if (nlookup_fs_root(anp, &nvp) == true)
165 		goto mounted;
166 
167 	mtx_lock_ex_quick(&autofs_softc->sc_lock);
168 	error = autofs_trigger(anp, path, pathlen);
169 	mtx_unlock_ex(&autofs_softc->sc_lock);
170 
171 	if (error)
172 		return (error);
173 
174 	if (nlookup_fs_root(anp, &nvp) == false)
175 		return (0);
176 
177 	/*
178 	 * If the operation that succeeded was mount, then mark
179 	 * the node as non-cached.  Otherwise, if someone unmounts
180 	 * the filesystem before the cache times out, we will fail
181 	 * to trigger.
182 	 */
183 	autofs_node_uncache(anp);
184 mounted:
185 	*newvp = nvp;
186 	KKASSERT(vn_islocked(*newvp));
187 
188 	return (0);
189 }
190 
191 static int
192 autofs_nresolve(struct vop_nresolve_args *ap)
193 {
194 	struct vnode *vp = NULL;
195 	struct vnode *dvp = ap->a_dvp;
196 	struct nchandle *nch = ap->a_nch;
197 	struct namecache *ncp = nch->ncp;
198 	struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
199 	struct autofs_node *anp = VTOI(dvp);
200 	struct autofs_node *child = NULL;
201 	int error;
202 
203 	if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false &&
204 	    autofs_ignore_thread() == false) {
205 		struct vnode *newvp = NULL;
206 
207 		cache_hold(nch);
208 		cache_unlock(nch);
209 		error = autofs_trigger_vn(dvp, ncp->nc_name, ncp->nc_nlen,
210 		    &newvp);
211 		cache_lock(nch);
212 		cache_drop(nch);
213 		if (error)
214 			return (error);
215 		if (newvp != NULL) {
216 			KKASSERT(newvp->v_tag != VT_AUTOFS);
217 			vput(newvp);
218 			return (ESTALE);
219 		}
220 		return (0);
221 	}
222 
223 	mtx_lock_sh_quick(&amp->am_lock);
224 	error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
225 	mtx_unlock_sh(&amp->am_lock);
226 
227 	if (error) {
228 		cache_setvp(nch, NULL);
229 		return (0);
230 	}
231 
232 	error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
233 	if (error == 0) {
234 		KKASSERT(vn_islocked(vp));
235 		vn_unlock(vp);
236 		cache_setvp(nch, vp);
237 		vrele(vp);
238 		return (0);
239 	}
240 
241 	return (error);
242 }
243 
244 static int
245 autofs_nmkdir(struct vop_nmkdir_args *ap)
246 {
247 	struct vnode *vp = NULL;
248 	struct vnode *dvp = ap->a_dvp;
249 	struct nchandle *nch = ap->a_nch;
250 	struct namecache *ncp = nch->ncp;
251 	struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
252 	struct autofs_node *anp = VTOI(dvp);
253 	struct autofs_node *child = NULL;
254 	int error;
255 
256 	/*
257 	 * Do not allow mkdir() if the calling thread is not
258 	 * automountd(8) descendant.
259 	 */
260 	if (autofs_ignore_thread() == false)
261 		return (EPERM);
262 
263 	mtx_lock_ex_quick(&amp->am_lock);
264 	error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
265 	mtx_unlock_ex(&amp->am_lock);
266 	KKASSERT(error == 0);
267 
268 	error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
269 	if (error == 0) {
270 		KKASSERT(vn_islocked(vp));
271 		cache_setunresolved(nch);
272 		cache_setvp(nch, vp);
273 		*(ap->a_vpp) = vp;
274 		return (0);
275 	}
276 
277 	return (error);
278 }
279 
280 static __inline size_t
281 autofs_dirent_reclen(const char *name)
282 {
283 	return (_DIRENT_RECLEN(strlen(name)));
284 }
285 
286 static int
287 autofs_write_dirent(struct uio *uio, const char *name, ino_t ino)
288 {
289 	int error = 0;
290 
291 	if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name))
292 		return (-1);
293 
294 	return (error);
295 }
296 
297 static int
298 autofs_readdir(struct vop_readdir_args *ap)
299 {
300 	struct vnode *vp = ap->a_vp;
301 	struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount);
302 	struct autofs_node *anp = VTOI(vp);
303 	struct autofs_node *child;
304 	struct uio *uio = ap->a_uio;
305 	ssize_t initial_resid = ap->a_uio->uio_resid;
306 	size_t reclens;
307 	int error;
308 
309 	KASSERT(vp->v_type == VDIR, ("!VDIR"));
310 
311 	if (autofs_cached(anp, NULL, 0) == false &&
312 	    autofs_ignore_thread() == false) {
313 		struct vnode *newvp = NULL;
314 		error = autofs_trigger_vn(vp, "", 0, &newvp);
315 		if (error)
316 			return (error);
317 		if (newvp != NULL) {
318 			KKASSERT(newvp->v_tag != VT_AUTOFS);
319 			vn_unlock(newvp);
320 			error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
321 			    ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
322 			vrele(newvp);
323 			return (error);
324 		}
325 	}
326 
327 	if (uio->uio_offset < 0)
328 		return (EINVAL);
329 
330 	if (ap->a_eofflag != NULL)
331 		*ap->a_eofflag = FALSE;
332 
333 	/*
334 	 * Write out the directory entry for ".".
335 	 */
336 	if (uio->uio_offset == 0) {
337 		error = autofs_write_dirent(uio, ".", anp->an_ino);
338 		if (error) {
339 			if (error == -1)
340 				error = 0;
341 			goto out;
342 		}
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_write_dirent(uio, "..",
353 		    anp->an_parent ? anp->an_parent->an_ino : anp->an_ino);
354 		if (error) {
355 			if (error == -1)
356 				error = 0;
357 			goto out;
358 		}
359 	}
360 	reclens += autofs_dirent_reclen("..");
361 
362 	/*
363 	 * Write out the directory entries for subdirectories.
364 	 */
365 	mtx_lock_sh_quick(&amp->am_lock);
366 	RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
367 		/*
368 		 * Check the offset to skip entries returned by previous
369 		 * calls to getdents().
370 		 */
371 		if (uio->uio_offset > reclens) {
372 			reclens += autofs_dirent_reclen(child->an_name);
373 			continue;
374 		}
375 
376 		/*
377 		 * Prevent seeking into the middle of dirent.
378 		 */
379 		if (uio->uio_offset != reclens) {
380 			mtx_unlock_sh(&amp->am_lock);
381 			return (EINVAL);
382 		}
383 
384 		error = autofs_write_dirent(uio, child->an_name, child->an_ino);
385 		reclens += autofs_dirent_reclen(child->an_name);
386 		if (error) {
387 			if (error == -1)
388 				error = 0;
389 			mtx_unlock_sh(&amp->am_lock);
390 			goto out;
391 		}
392 	}
393 	mtx_unlock_sh(&amp->am_lock);
394 
395 	if (ap->a_eofflag != NULL)
396 		*ap->a_eofflag = TRUE;
397 
398 	return (0);
399 out:
400 	/*
401 	 * Return error if the initial buffer was too small to do anything.
402 	 */
403 	if (uio->uio_resid == initial_resid)
404 		return (error);
405 
406 	/*
407 	 * Don't return an error if we managed to copy out some entries.
408 	 */
409 	if (uio->uio_resid < initial_resid)
410 		return (0);
411 
412 	return (error);
413 }
414 
415 static int
416 autofs_reclaim(struct vop_reclaim_args *ap)
417 {
418 	struct vnode *vp = ap->a_vp;
419 	struct autofs_node *anp = VTOI(vp);
420 
421 	/*
422 	 * We do not free autofs_node here; instead we are
423 	 * destroying them in autofs_node_delete().
424 	 */
425 	mtx_lock_ex_quick(&anp->an_vnode_lock);
426 	anp->an_vnode = NULL;
427 	vp->v_data = NULL;
428 	mtx_unlock_ex(&anp->an_vnode_lock);
429 
430 	return (0);
431 }
432 
433 static int
434 autofs_mountctl(struct vop_mountctl_args *ap)
435 {
436 	struct mount *mp;
437 	int res = 0;
438 
439 	mp = ap->a_head.a_ops->head.vv_mount;
440 	lwkt_gettoken(&mp->mnt_token);
441 
442 	switch (ap->a_op) {
443 	//case ...:
444 	//	break;
445 	default:
446 		res = vop_stdmountctl(ap);
447 		break;
448 	}
449 
450 	lwkt_reltoken(&mp->mnt_token);
451 	return (res);
452 }
453 
454 static int
455 autofs_print(struct vop_print_args *ap)
456 {
457 	struct autofs_node *anp = VTOI(ap->a_vp);
458 
459 	kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
460 	    anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached,
461 	    anp->an_retries, anp->an_wildcards);
462 
463 	return (0);
464 }
465 
466 struct vop_ops autofs_vnode_vops = {
467 	.vop_default =		vop_defaultop,
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, int namelen,
521     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 	vx_downgrade(vp);	/* downgrade VX lock to VN lock */
600 	*vpp = vp;
601 
602 	return (0);
603 }
604