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