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
test_fs_root(struct vnode * vp)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
nlookup_fs_root(struct autofs_node * anp,struct vnode ** vpp)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
autofs_access(struct vop_access_args * ap)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
autofs_getattr(struct vop_getattr_args * ap)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
autofs_trigger_vn(struct vnode * vp,const char * path,int pathlen,struct vnode ** newvp)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
autofs_nresolve(struct vop_nresolve_args * ap)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(&->am_lock);
224 error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
225 mtx_unlock_sh(&->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
autofs_nmkdir(struct vop_nmkdir_args * ap)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(&->am_lock);
264 error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
265 mtx_unlock_ex(&->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
autofs_dirent_reclen(const char * name)281 autofs_dirent_reclen(const char *name)
282 {
283 return (_DIRENT_RECLEN(strlen(name)));
284 }
285
286 static int
autofs_write_dirent(struct uio * uio,const char * name,ino_t ino)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
autofs_readdir(struct vop_readdir_args * ap)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(&->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(&->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(&->am_lock);
390 goto out;
391 }
392 }
393 mtx_unlock_sh(&->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
autofs_reclaim(struct vop_reclaim_args * ap)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
autofs_mountctl(struct vop_mountctl_args * ap)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
autofs_print(struct vop_print_args * ap)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
autofs_node_new(struct autofs_node * parent,struct autofs_mount * amp,const char * name,int namelen,struct autofs_node ** anpp)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(&->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
autofs_node_find(struct autofs_node * parent,const char * name,int namelen,struct autofs_node ** anpp)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
autofs_node_delete(struct autofs_node * anp)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
autofs_node_vn(struct autofs_node * anp,struct mount * mp,int flags,struct vnode ** vpp)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