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