1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2014 The FreeBSD Foundation
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/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/condvar.h>
36 #include <sys/dirent.h>
37 #include <sys/fcntl.h>
38 #include <sys/lock.h>
39 #include <sys/mount.h>
40 #include <sys/mutex.h>
41 #include <sys/namei.h>
42 #include <sys/signalvar.h>
43 #include <sys/stat.h>
44 #include <sys/taskqueue.h>
45 #include <sys/tree.h>
46 #include <sys/vnode.h>
47 #include <machine/atomic.h>
48 #include <vm/uma.h>
49
50 #include <fs/autofs/autofs.h>
51
52 static int autofs_trigger_vn(struct vnode *vp, const char *path,
53 int pathlen, struct vnode **newvp);
54
55 extern struct autofs_softc *autofs_softc;
56
57 static int
autofs_access(struct vop_access_args * ap)58 autofs_access(struct vop_access_args *ap)
59 {
60
61 /*
62 * Nothing to do here; the only kind of access control
63 * needed is in autofs_mkdir().
64 */
65
66 return (0);
67 }
68
69 static int
autofs_getattr(struct vop_getattr_args * ap)70 autofs_getattr(struct vop_getattr_args *ap)
71 {
72 struct vnode *vp, *newvp;
73 struct autofs_node *anp;
74 struct mount *mp;
75 struct vattr *vap;
76 int error;
77
78 vp = ap->a_vp;
79 anp = vp->v_data;
80 mp = vp->v_mount;
81 vap = ap->a_vap;
82
83 KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR"));
84
85 /*
86 * The reason we must do this is that some tree-walking software,
87 * namely fts(3), assumes that stat(".") results will not change
88 * between chdir("subdir") and chdir(".."), and fails with ENOENT
89 * otherwise.
90 */
91 if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false &&
92 autofs_ignore_thread(curthread) == false) {
93 error = autofs_trigger_vn(vp, "", 0, &newvp);
94 if (error != 0)
95 return (error);
96
97 if (newvp != NULL) {
98 error = VOP_GETATTR(newvp, ap->a_vap,
99 ap->a_cred);
100 vput(newvp);
101 return (error);
102 }
103 }
104
105 vap->va_type = VDIR;
106 vap->va_mode = 0755;
107 vap->va_nlink = 3; /* XXX */
108 vap->va_uid = 0;
109 vap->va_gid = 0;
110 vap->va_rdev = NODEV;
111 vap->va_fsid = mp->mnt_stat.f_fsid.val[0];
112 vap->va_fileid = anp->an_fileno;
113 vap->va_size = S_BLKSIZE;
114 vap->va_blocksize = S_BLKSIZE;
115 vap->va_mtime = anp->an_ctime;
116 vap->va_atime = anp->an_ctime;
117 vap->va_ctime = anp->an_ctime;
118 vap->va_birthtime = anp->an_ctime;
119 vap->va_gen = 0;
120 vap->va_flags = 0;
121 vap->va_rdev = 0;
122 vap->va_bytes = S_BLKSIZE;
123 vap->va_filerev = 0;
124 vap->va_spare = 0;
125
126 return (0);
127 }
128
129 /*
130 * Unlock the vnode, request automountd(8) action, and then lock it back.
131 * If anything got mounted on top of the vnode, return the new filesystem's
132 * root vnode in 'newvp', locked.
133 */
134 static int
autofs_trigger_vn(struct vnode * vp,const char * path,int pathlen,struct vnode ** newvp)135 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
136 struct vnode **newvp)
137 {
138 struct autofs_node *anp;
139 int error, lock_flags;
140
141 anp = vp->v_data;
142
143 /*
144 * Release the vnode lock, so that other operations, in partcular
145 * mounting a filesystem on top of it, can proceed. Increase use
146 * count, to prevent the vnode from being deallocated and to prevent
147 * filesystem from being unmounted.
148 */
149 lock_flags = VOP_ISLOCKED(vp);
150 vref(vp);
151 VOP_UNLOCK(vp);
152
153 sx_xlock(&autofs_softc->sc_lock);
154
155 /*
156 * XXX: Workaround for mounting the same thing multiple times; revisit.
157 */
158 if (vp->v_mountedhere != NULL) {
159 error = 0;
160 goto mounted;
161 }
162
163 error = autofs_trigger(anp, path, pathlen);
164 mounted:
165 sx_xunlock(&autofs_softc->sc_lock);
166 vn_lock(vp, lock_flags | LK_RETRY);
167 vunref(vp);
168 if (VN_IS_DOOMED(vp)) {
169 AUTOFS_DEBUG("VIRF_DOOMED");
170 return (ENOENT);
171 }
172
173 if (error != 0)
174 return (error);
175
176 if (vp->v_mountedhere == NULL) {
177 *newvp = NULL;
178 return (0);
179 } else {
180 /*
181 * If the operation that succeeded was mount, then mark
182 * the node as non-cached. Otherwise, if someone unmounts
183 * the filesystem before the cache times out, we will fail
184 * to trigger.
185 */
186 anp->an_cached = false;
187 }
188
189 error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp);
190 if (error != 0) {
191 AUTOFS_WARN("VFS_ROOT() failed with error %d", error);
192 return (error);
193 }
194
195 return (0);
196 }
197
198 static int
autofs_vget_callback(struct mount * mp,void * arg,int flags,struct vnode ** vpp)199 autofs_vget_callback(struct mount *mp, void *arg, int flags,
200 struct vnode **vpp)
201 {
202
203 return (autofs_node_vn(arg, mp, flags, vpp));
204 }
205
206 static int
autofs_lookup(struct vop_lookup_args * ap)207 autofs_lookup(struct vop_lookup_args *ap)
208 {
209 struct vnode *dvp, *newvp, **vpp;
210 struct mount *mp;
211 struct autofs_mount *amp;
212 struct autofs_node *anp, *child;
213 struct componentname *cnp;
214 int error;
215
216 dvp = ap->a_dvp;
217 vpp = ap->a_vpp;
218 mp = dvp->v_mount;
219 amp = VFSTOAUTOFS(mp);
220 anp = dvp->v_data;
221 cnp = ap->a_cnp;
222
223 if (cnp->cn_flags & ISDOTDOT) {
224 KASSERT(anp->an_parent != NULL, ("NULL parent"));
225 /*
226 * Note that in this case, dvp is the child vnode, and we
227 * are looking up the parent vnode - exactly reverse from
228 * normal operation. Unlocking dvp requires some rather
229 * tricky unlock/relock dance to prevent mp from being freed;
230 * use vn_vget_ino_gen() which takes care of all that.
231 */
232 error = vn_vget_ino_gen(dvp, autofs_vget_callback,
233 anp->an_parent, cnp->cn_lkflags, vpp);
234 if (error != 0) {
235 AUTOFS_WARN("vn_vget_ino_gen() failed with error %d",
236 error);
237 return (error);
238 }
239 return (error);
240 }
241
242 if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
243 vref(dvp);
244 *vpp = dvp;
245
246 return (0);
247 }
248
249 if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false &&
250 autofs_ignore_thread(curthread) == false) {
251 error = autofs_trigger_vn(dvp,
252 cnp->cn_nameptr, cnp->cn_namelen, &newvp);
253 if (error != 0)
254 return (error);
255
256 if (newvp != NULL) {
257 /*
258 * The target filesystem got automounted.
259 * Let the lookup(9) go around with the same
260 * path component.
261 */
262 vput(newvp);
263 return (ERELOOKUP);
264 }
265 }
266
267 AUTOFS_SLOCK(amp);
268 error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child);
269 if (error != 0) {
270 if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
271 AUTOFS_SUNLOCK(amp);
272 return (EJUSTRETURN);
273 }
274
275 AUTOFS_SUNLOCK(amp);
276 return (ENOENT);
277 }
278
279 /*
280 * XXX: Dropping the node here is ok, because we never remove nodes.
281 */
282 AUTOFS_SUNLOCK(amp);
283
284 error = autofs_node_vn(child, mp, cnp->cn_lkflags, vpp);
285 if (error != 0) {
286 if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE)
287 return (EJUSTRETURN);
288
289 return (error);
290 }
291
292 return (0);
293 }
294
295 static int
autofs_mkdir(struct vop_mkdir_args * ap)296 autofs_mkdir(struct vop_mkdir_args *ap)
297 {
298 struct vnode *vp;
299 struct autofs_node *anp;
300 struct autofs_mount *amp;
301 struct autofs_node *child;
302 int error;
303
304 vp = ap->a_dvp;
305 anp = vp->v_data;
306 amp = VFSTOAUTOFS(vp->v_mount);
307
308 /*
309 * Do not allow mkdir() if the calling thread is not
310 * automountd(8) descendant.
311 */
312 if (autofs_ignore_thread(curthread) == false)
313 return (EPERM);
314
315 AUTOFS_XLOCK(amp);
316 error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr,
317 ap->a_cnp->cn_namelen, &child);
318 if (error != 0) {
319 AUTOFS_XUNLOCK(amp);
320 return (error);
321 }
322 AUTOFS_XUNLOCK(amp);
323
324 error = autofs_node_vn(child, vp->v_mount, LK_EXCLUSIVE, ap->a_vpp);
325
326 return (error);
327 }
328
329 static int
autofs_print(struct vop_print_args * ap)330 autofs_print(struct vop_print_args *ap)
331 {
332 struct vnode *vp;
333 struct autofs_node *anp;
334
335 vp = ap->a_vp;
336 anp = vp->v_data;
337
338 printf(" name \"%s\", fileno %d, cached %d, wildcards %d\n",
339 anp->an_name, anp->an_fileno, anp->an_cached, anp->an_wildcards);
340
341 return (0);
342 }
343
344 /*
345 * Write out a single 'struct dirent', based on 'name' and 'fileno' arguments.
346 */
347 static int
autofs_readdir_one(struct uio * uio,const char * name,int fileno,size_t * reclenp)348 autofs_readdir_one(struct uio *uio, const char *name, int fileno,
349 size_t *reclenp)
350 {
351 struct dirent dirent;
352 size_t namlen, reclen;
353 int error;
354
355 namlen = strlen(name);
356 reclen = _GENERIC_DIRLEN(namlen);
357 if (reclenp != NULL)
358 *reclenp = reclen;
359
360 if (uio == NULL)
361 return (0);
362
363 if (uio->uio_resid < reclen)
364 return (EINVAL);
365
366 dirent.d_fileno = fileno;
367 dirent.d_off = uio->uio_offset + reclen;
368 dirent.d_reclen = reclen;
369 dirent.d_type = DT_DIR;
370 dirent.d_namlen = namlen;
371 memcpy(dirent.d_name, name, namlen);
372 dirent_terminate(&dirent);
373 error = uiomove(&dirent, reclen, uio);
374
375 return (error);
376 }
377
378 static size_t
autofs_dirent_reclen(const char * name)379 autofs_dirent_reclen(const char *name)
380 {
381 size_t reclen;
382
383 (void)autofs_readdir_one(NULL, name, -1, &reclen);
384
385 return (reclen);
386 }
387
388 static int
autofs_readdir(struct vop_readdir_args * ap)389 autofs_readdir(struct vop_readdir_args *ap)
390 {
391 struct vnode *vp, *newvp;
392 struct autofs_mount *amp;
393 struct autofs_node *anp, *child;
394 struct uio *uio;
395 size_t reclen, reclens;
396 ssize_t initial_resid;
397 int error;
398
399 vp = ap->a_vp;
400 amp = VFSTOAUTOFS(vp->v_mount);
401 anp = vp->v_data;
402 uio = ap->a_uio;
403 initial_resid = ap->a_uio->uio_resid;
404
405 KASSERT(vp->v_type == VDIR, ("!VDIR"));
406
407 if (autofs_cached(anp, NULL, 0) == false &&
408 autofs_ignore_thread(curthread) == false) {
409 error = autofs_trigger_vn(vp, "", 0, &newvp);
410 if (error != 0)
411 return (error);
412
413 if (newvp != NULL) {
414 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
415 ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
416 vput(newvp);
417 return (error);
418 }
419 }
420
421 if (uio->uio_offset < 0)
422 return (EINVAL);
423
424 if (ap->a_eofflag != NULL)
425 *ap->a_eofflag = FALSE;
426
427 /*
428 * Write out the directory entry for ".". This is conditional
429 * on the current offset into the directory; same applies to the
430 * other two cases below.
431 */
432 if (uio->uio_offset == 0) {
433 error = autofs_readdir_one(uio, ".", anp->an_fileno, &reclen);
434 if (error != 0)
435 goto out;
436 }
437 reclens = autofs_dirent_reclen(".");
438
439 /*
440 * Write out the directory entry for "..".
441 */
442 if (uio->uio_offset <= reclens) {
443 if (uio->uio_offset != reclens)
444 return (EINVAL);
445 if (anp->an_parent == NULL) {
446 error = autofs_readdir_one(uio, "..",
447 anp->an_fileno, &reclen);
448 } else {
449 error = autofs_readdir_one(uio, "..",
450 anp->an_parent->an_fileno, &reclen);
451 }
452 if (error != 0)
453 goto out;
454 }
455
456 reclens += autofs_dirent_reclen("..");
457
458 /*
459 * Write out the directory entries for subdirectories.
460 */
461 AUTOFS_SLOCK(amp);
462 RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
463 /*
464 * Check the offset to skip entries returned by previous
465 * calls to getdents().
466 */
467 if (uio->uio_offset > reclens) {
468 reclens += autofs_dirent_reclen(child->an_name);
469 continue;
470 }
471
472 /*
473 * Prevent seeking into the middle of dirent.
474 */
475 if (uio->uio_offset != reclens) {
476 AUTOFS_SUNLOCK(amp);
477 return (EINVAL);
478 }
479
480 error = autofs_readdir_one(uio, child->an_name,
481 child->an_fileno, &reclen);
482 reclens += reclen;
483 if (error != 0) {
484 AUTOFS_SUNLOCK(amp);
485 goto out;
486 }
487 }
488 AUTOFS_SUNLOCK(amp);
489
490 if (ap->a_eofflag != NULL)
491 *ap->a_eofflag = TRUE;
492
493 return (0);
494
495 out:
496 /*
497 * Return error if the initial buffer was too small to do anything.
498 */
499 if (uio->uio_resid == initial_resid)
500 return (error);
501
502 /*
503 * Don't return an error if we managed to copy out some entries.
504 */
505 if (uio->uio_resid < reclen)
506 return (0);
507
508 return (error);
509 }
510
511 static int
autofs_reclaim(struct vop_reclaim_args * ap)512 autofs_reclaim(struct vop_reclaim_args *ap)
513 {
514 struct vnode *vp;
515 struct autofs_node *anp;
516
517 vp = ap->a_vp;
518 anp = vp->v_data;
519
520 /*
521 * We do not free autofs_node here; instead we are
522 * destroying them in autofs_node_delete().
523 */
524 sx_xlock(&anp->an_vnode_lock);
525 anp->an_vnode = NULL;
526 vp->v_data = NULL;
527 sx_xunlock(&anp->an_vnode_lock);
528
529 return (0);
530 }
531
532 struct vop_vector autofs_vnodeops = {
533 .vop_default = &default_vnodeops,
534
535 .vop_access = autofs_access,
536 .vop_lookup = autofs_lookup,
537 .vop_create = VOP_EOPNOTSUPP,
538 .vop_getattr = autofs_getattr,
539 .vop_link = VOP_EOPNOTSUPP,
540 .vop_mkdir = autofs_mkdir,
541 .vop_mknod = VOP_EOPNOTSUPP,
542 .vop_print = autofs_print,
543 .vop_read = VOP_EOPNOTSUPP,
544 .vop_readdir = autofs_readdir,
545 .vop_remove = VOP_EOPNOTSUPP,
546 .vop_rename = VOP_EOPNOTSUPP,
547 .vop_rmdir = VOP_EOPNOTSUPP,
548 .vop_setattr = VOP_EOPNOTSUPP,
549 .vop_symlink = VOP_EOPNOTSUPP,
550 .vop_write = VOP_EOPNOTSUPP,
551 .vop_reclaim = autofs_reclaim,
552 };
553 VFS_VOP_VECTOR_REGISTER(autofs_vnodeops);
554
555 int
autofs_node_new(struct autofs_node * parent,struct autofs_mount * amp,const char * name,int namelen,struct autofs_node ** anpp)556 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
557 const char *name, int namelen, struct autofs_node **anpp)
558 {
559 struct autofs_node *anp;
560
561 if (parent != NULL) {
562 AUTOFS_ASSERT_XLOCKED(parent->an_mount);
563
564 KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT,
565 ("node \"%s\" already exists", name));
566 }
567
568 anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO);
569 if (namelen >= 0)
570 anp->an_name = strndup(name, namelen, M_AUTOFS);
571 else
572 anp->an_name = strdup(name, M_AUTOFS);
573 anp->an_fileno = atomic_fetchadd_int(&->am_last_fileno, 1);
574 callout_init(&anp->an_callout, 1);
575 /*
576 * The reason for SX_NOWITNESS here is that witness(4)
577 * cannot tell vnodes apart, so the following perfectly
578 * valid lock order...
579 *
580 * vnode lock A -> autofsvlk B -> vnode lock B
581 *
582 * ... gets reported as a LOR.
583 */
584 sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS);
585 getnanotime(&anp->an_ctime);
586 anp->an_parent = parent;
587 anp->an_mount = amp;
588 if (parent != NULL)
589 RB_INSERT(autofs_node_tree, &parent->an_children, anp);
590 RB_INIT(&anp->an_children);
591
592 *anpp = anp;
593 return (0);
594 }
595
596 int
autofs_node_find(struct autofs_node * parent,const char * name,int namelen,struct autofs_node ** anpp)597 autofs_node_find(struct autofs_node *parent, const char *name,
598 int namelen, struct autofs_node **anpp)
599 {
600 struct autofs_node *anp, find;
601 int error;
602
603 AUTOFS_ASSERT_LOCKED(parent->an_mount);
604
605 if (namelen >= 0)
606 find.an_name = strndup(name, namelen, M_AUTOFS);
607 else
608 find.an_name = strdup(name, M_AUTOFS);
609
610 anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
611 if (anp != NULL) {
612 error = 0;
613 if (anpp != NULL)
614 *anpp = anp;
615 } else {
616 error = ENOENT;
617 }
618
619 free(find.an_name, M_AUTOFS);
620
621 return (error);
622 }
623
624 void
autofs_node_delete(struct autofs_node * anp)625 autofs_node_delete(struct autofs_node *anp)
626 {
627 struct autofs_node *parent;
628
629 AUTOFS_ASSERT_XLOCKED(anp->an_mount);
630 KASSERT(RB_EMPTY(&anp->an_children), ("have children"));
631
632 callout_drain(&anp->an_callout);
633
634 parent = anp->an_parent;
635 if (parent != NULL)
636 RB_REMOVE(autofs_node_tree, &parent->an_children, anp);
637 sx_destroy(&anp->an_vnode_lock);
638 free(anp->an_name, M_AUTOFS);
639 uma_zfree(autofs_node_zone, anp);
640 }
641
642 int
autofs_node_vn(struct autofs_node * anp,struct mount * mp,int flags,struct vnode ** vpp)643 autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags,
644 struct vnode **vpp)
645 {
646 struct vnode *vp;
647 int error;
648
649 AUTOFS_ASSERT_UNLOCKED(anp->an_mount);
650
651 sx_xlock(&anp->an_vnode_lock);
652
653 vp = anp->an_vnode;
654 if (vp != NULL) {
655 error = vget(vp, flags | LK_RETRY);
656 if (error != 0) {
657 AUTOFS_WARN("vget failed with error %d", error);
658 sx_xunlock(&anp->an_vnode_lock);
659 return (error);
660 }
661 if (VN_IS_DOOMED(vp)) {
662 /*
663 * We got forcibly unmounted.
664 */
665 AUTOFS_DEBUG("doomed vnode");
666 sx_xunlock(&anp->an_vnode_lock);
667 vput(vp);
668
669 return (ENOENT);
670 }
671
672 *vpp = vp;
673 sx_xunlock(&anp->an_vnode_lock);
674 return (0);
675 }
676
677 error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp);
678 if (error != 0) {
679 sx_xunlock(&anp->an_vnode_lock);
680 return (error);
681 }
682
683 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
684
685 vp->v_type = VDIR;
686 if (anp->an_parent == NULL)
687 vp->v_vflag |= VV_ROOT;
688 vp->v_data = anp;
689
690 VN_LOCK_ASHARE(vp);
691
692 error = insmntque(vp, mp);
693 if (error != 0) {
694 AUTOFS_DEBUG("insmntque() failed with error %d", error);
695 sx_xunlock(&anp->an_vnode_lock);
696 return (error);
697 }
698
699 KASSERT(anp->an_vnode == NULL, ("lost race"));
700 anp->an_vnode = vp;
701
702 sx_xunlock(&anp->an_vnode_lock);
703
704 vn_set_state(vp, VSTATE_CONSTRUCTED);
705 *vpp = vp;
706 return (0);
707 }
708