2 * Copyright (c) 2016 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2016 The DragonFly Project
4 * Copyright (c) 2014 The FreeBSD Foundation
7 * This software was developed by Edward Tomasz Napierala under sponsorship
8 * from the FreeBSD Foundation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <sys/kernel.h>
34 #include <sys/module.h>
36 #include <sys/dirent.h>
37 #include <sys/nlookup.h>
38 #include <sys/mountctl.h>
43 test_fs_root(struct vnode
*vp
)
47 if ((error
= vget(vp
, LK_SHARED
)) != 0) {
48 AUTOFS_WARN("vget failed with error %d", error
);
52 if (((vp
->v_flag
& VROOT
) == 0) || (vp
->v_tag
== VT_AUTOFS
)) {
61 nlookup_fs_root(struct autofs_node
*anp
, struct vnode
**vpp
)
63 struct nlookupdata nd
;
67 path
= autofs_path(anp
);
69 error
= nlookup_init(&nd
, path
, UIO_SYSSPACE
, NLC_FOLLOW
);
73 struct vnode
*vp
= nd
.nl_nch
.ncp
->nc_vp
;
74 if (test_fs_root(vp
) == true)
81 kfree(path
, M_AUTOFS
);
83 return (error
? false : true);
87 autofs_access(struct vop_access_args
*ap
)
90 * Nothing to do here; the only kind of access control
91 * needed is in autofs_mkdir().
97 autofs_getattr(struct vop_getattr_args
*ap
)
99 struct vnode
*vp
= ap
->a_vp
;
100 struct vattr
*vap
= ap
->a_vap
;
101 struct autofs_node
*anp
= VTOI(vp
);
103 KASSERT(vp
->v_type
== VDIR
, ("!VDIR"));
106 * The reason we must do this is that some tree-walking software,
107 * namely fts(3), assumes that stat(".") results will not change
108 * between chdir("subdir") and chdir(".."), and fails with ENOENT
111 * XXX: Not supported on DragonFly.
112 * With the current trigger mechanism on DragonFly, the process
113 * will hang while in nlookup() in nlookup_fs_root().
116 if (autofs_mount_on_stat
&& ...) {
120 vap
->va_nlink
= 3; /* XXX: FreeBSD had it like this */
124 vap
->va_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
.val
[0];
125 vap
->va_fileid
= anp
->an_ino
;
126 vap
->va_size
= S_BLKSIZE
;
127 vap
->va_blocksize
= S_BLKSIZE
;
128 vap
->va_atime
= anp
->an_ctime
;
129 vap
->va_mtime
= anp
->an_ctime
;
130 vap
->va_ctime
= anp
->an_ctime
;
135 vap
->va_bytes
= S_BLKSIZE
;
144 autofs_trigger_vn(struct vnode
*vp
, const char *path
, int pathlen
,
145 struct vnode
**newvp
)
147 struct autofs_node
*anp
= VTOI(vp
);
148 struct vnode
*nvp
= NULL
;
151 KKASSERT(!vn_islocked(vp
));
153 if (test_fs_root(vp
) == true)
157 * Don't remove this lookup. Without this, the process
158 * may trigger automountd(8) to mount the filesystem for the
159 * second time after successful mount, and the second attempt
162 if (nlookup_fs_root(anp
, &nvp
) == true)
165 mtx_lock_ex_quick(&autofs_softc
->sc_lock
);
166 error
= autofs_trigger(anp
, path
, pathlen
);
167 mtx_unlock_ex(&autofs_softc
->sc_lock
);
172 if (nlookup_fs_root(anp
, &nvp
) == false)
176 * If the operation that succeeded was mount, then mark
177 * the node as non-cached. Otherwise, if someone unmounts
178 * the filesystem before the cache times out, we will fail
181 autofs_node_uncache(anp
);
184 KKASSERT(vn_islocked(*newvp
));
190 autofs_nresolve(struct vop_nresolve_args
*ap
)
192 struct vnode
*vp
= NULL
;
193 struct vnode
*dvp
= ap
->a_dvp
;
194 struct nchandle
*nch
= ap
->a_nch
;
195 struct namecache
*ncp
= nch
->ncp
;
196 struct autofs_mount
*amp
= VFSTOAUTOFS(dvp
->v_mount
);
197 struct autofs_node
*anp
= VTOI(dvp
);
198 struct autofs_node
*child
= NULL
;
201 if (autofs_cached(anp
, ncp
->nc_name
, ncp
->nc_nlen
) == false &&
202 autofs_ignore_thread() == false) {
203 struct vnode
*newvp
= NULL
;
207 error
= autofs_trigger_vn(dvp
, ncp
->nc_name
, ncp
->nc_nlen
,
214 KKASSERT(newvp
->v_tag
!= VT_AUTOFS
);
221 mtx_lock_sh_quick(&
->am_lock
);
222 error
= autofs_node_find(anp
, ncp
->nc_name
, ncp
->nc_nlen
, &child
);
223 mtx_unlock_sh(&
->am_lock
);
226 cache_setvp(nch
, NULL
);
230 error
= autofs_node_vn(child
, dvp
->v_mount
, LK_EXCLUSIVE
, &vp
);
232 KKASSERT(vn_islocked(vp
));
234 cache_setvp(nch
, vp
);
243 autofs_nmkdir(struct vop_nmkdir_args
*ap
)
245 struct vnode
*vp
= NULL
;
246 struct vnode
*dvp
= ap
->a_dvp
;
247 struct nchandle
*nch
= ap
->a_nch
;
248 struct namecache
*ncp
= nch
->ncp
;
249 struct autofs_mount
*amp
= VFSTOAUTOFS(dvp
->v_mount
);
250 struct autofs_node
*anp
= VTOI(dvp
);
251 struct autofs_node
*child
= NULL
;
255 * Do not allow mkdir() if the calling thread is not
256 * automountd(8) descendant.
258 if (autofs_ignore_thread() == false)
261 mtx_lock_ex_quick(&
->am_lock
);
262 error
= autofs_node_new(anp
, amp
, ncp
->nc_name
, ncp
->nc_nlen
, &child
);
263 mtx_unlock_ex(&
->am_lock
);
264 KKASSERT(error
== 0);
266 error
= autofs_node_vn(child
, dvp
->v_mount
, LK_EXCLUSIVE
, &vp
);
268 KKASSERT(vn_islocked(vp
));
269 cache_setunresolved(nch
);
270 cache_setvp(nch
, vp
);
278 static __inline
size_t
279 autofs_dirent_reclen(const char *name
)
281 return (_DIRENT_RECLEN(strlen(name
)));
285 autofs_write_dirent(struct uio
*uio
, const char *name
, ino_t ino
)
289 if (vop_write_dirent(&error
, uio
, ino
, DT_DIR
, strlen(name
), name
))
296 autofs_readdir(struct vop_readdir_args
*ap
)
298 struct vnode
*vp
= ap
->a_vp
;
299 struct autofs_mount
*amp
= VFSTOAUTOFS(vp
->v_mount
);
300 struct autofs_node
*anp
= VTOI(vp
);
301 struct autofs_node
*child
;
302 struct uio
*uio
= ap
->a_uio
;
303 ssize_t initial_resid
= ap
->a_uio
->uio_resid
;
307 KASSERT(vp
->v_type
== VDIR
, ("!VDIR"));
309 if (autofs_cached(anp
, NULL
, 0) == false &&
310 autofs_ignore_thread() == false) {
311 struct vnode
*newvp
= NULL
;
312 error
= autofs_trigger_vn(vp
, "", 0, &newvp
);
316 KKASSERT(newvp
->v_tag
!= VT_AUTOFS
);
318 error
= VOP_READDIR(newvp
, ap
->a_uio
, ap
->a_cred
,
319 ap
->a_eofflag
, ap
->a_ncookies
, ap
->a_cookies
);
325 if (uio
->uio_offset
< 0)
328 if (ap
->a_eofflag
!= NULL
)
329 *ap
->a_eofflag
= FALSE
;
332 * Write out the directory entry for ".".
334 if (uio
->uio_offset
== 0) {
335 error
= autofs_write_dirent(uio
, ".", anp
->an_ino
);
339 reclens
= autofs_dirent_reclen(".");
342 * Write out the directory entry for "..".
344 if (uio
->uio_offset
<= reclens
) {
345 if (uio
->uio_offset
!= reclens
)
347 error
= autofs_write_dirent(uio
, "..",
348 anp
->an_parent
? anp
->an_parent
->an_ino
: anp
->an_ino
);
352 reclens
+= autofs_dirent_reclen("..");
355 * Write out the directory entries for subdirectories.
357 mtx_lock_sh_quick(&
->am_lock
);
358 RB_FOREACH(child
, autofs_node_tree
, &anp
->an_children
) {
360 * Check the offset to skip entries returned by previous
361 * calls to getdents().
363 if (uio
->uio_offset
> reclens
) {
364 reclens
+= autofs_dirent_reclen(child
->an_name
);
369 * Prevent seeking into the middle of dirent.
371 if (uio
->uio_offset
!= reclens
) {
372 mtx_unlock_sh(&
->am_lock
);
376 error
= autofs_write_dirent(uio
, child
->an_name
, child
->an_ino
);
377 reclens
+= autofs_dirent_reclen(child
->an_name
);
379 mtx_unlock_sh(&
->am_lock
);
383 mtx_unlock_sh(&
->am_lock
);
385 if (ap
->a_eofflag
!= NULL
)
386 *ap
->a_eofflag
= TRUE
;
391 * Return error if the initial buffer was too small to do anything.
393 if (uio
->uio_resid
== initial_resid
)
397 * Don't return an error if we managed to copy out some entries.
399 if (uio
->uio_resid
< initial_resid
)
406 autofs_reclaim(struct vop_reclaim_args
*ap
)
408 struct vnode
*vp
= ap
->a_vp
;
409 struct autofs_node
*anp
= VTOI(vp
);
412 * We do not free autofs_node here; instead we are
413 * destroying them in autofs_node_delete().
415 mtx_lock_ex_quick(&anp
->an_vnode_lock
);
416 anp
->an_vnode
= NULL
;
418 mtx_unlock_ex(&anp
->an_vnode_lock
);
424 autofs_mountctl(struct vop_mountctl_args
*ap
)
429 mp
= ap
->a_head
.a_ops
->head
.vv_mount
;
430 lwkt_gettoken(&mp
->mnt_token
);
436 res
= vop_stdmountctl(ap
);
440 lwkt_reltoken(&mp
->mnt_token
);
445 autofs_print(struct vop_print_args
*ap
)
447 struct autofs_node
*anp
= VTOI(ap
->a_vp
);
449 kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
450 anp
, (intmax_t)anp
->an_ino
, anp
->an_name
, anp
->an_cached
,
451 anp
->an_retries
, anp
->an_wildcards
);
456 struct vop_ops autofs_vnode_vops
= {
457 .vop_default
= vop_defaultop
,
458 .vop_access
= autofs_access
,
459 .vop_getattr
= autofs_getattr
,
460 .vop_nresolve
= autofs_nresolve
,
461 .vop_nmkdir
= autofs_nmkdir
,
462 .vop_readdir
= autofs_readdir
,
463 .vop_reclaim
= autofs_reclaim
,
464 .vop_mountctl
= autofs_mountctl
,
465 .vop_print
= autofs_print
,
469 autofs_node_new(struct autofs_node
*parent
, struct autofs_mount
*amp
,
470 const char *name
, int namelen
, struct autofs_node
**anpp
)
472 struct autofs_node
*anp
;
474 KKASSERT(mtx_islocked_ex(&
->am_lock
));
476 if (parent
!= NULL
) {
477 KKASSERT(mtx_islocked_ex(&parent
->an_mount
->am_lock
));
478 KASSERT(autofs_node_find(parent
, name
, namelen
, NULL
) == ENOENT
,
479 ("node \"%s\" already exists", name
));
483 * All struct fields must be initialized.
485 anp
= objcache_get(autofs_node_objcache
, M_WAITOK
);
487 anp
->an_name
= kstrndup(name
, namelen
, M_AUTOFS
);
489 anp
->an_name
= kstrdup(name
, M_AUTOFS
);
490 anp
->an_ino
= amp
->am_last_ino
++;
491 callout_init(&anp
->an_callout
);
492 mtx_init(&anp
->an_vnode_lock
, "autofsvnlk");
493 getnanotime(&anp
->an_ctime
);
494 anp
->an_parent
= parent
;
496 anp
->an_vnode
= NULL
;
497 anp
->an_cached
= false;
498 anp
->an_wildcards
= false;
501 RB_INSERT(autofs_node_tree
, &parent
->an_children
, anp
);
502 RB_INIT(&anp
->an_children
);
510 autofs_node_find(struct autofs_node
*parent
, const char *name
, int namelen
,
511 struct autofs_node
**anpp
)
513 struct autofs_node
*anp
, find
;
516 KKASSERT(mtx_islocked(&parent
->an_mount
->am_lock
));
519 find
.an_name
= kstrndup(name
, namelen
, M_AUTOFS
);
521 find
.an_name
= kstrdup(name
, M_AUTOFS
);
523 anp
= RB_FIND(autofs_node_tree
, &parent
->an_children
, &find
);
532 kfree(find
.an_name
, M_AUTOFS
);
538 autofs_node_delete(struct autofs_node
*anp
)
540 KKASSERT(mtx_islocked_ex(&anp
->an_mount
->am_lock
));
541 KASSERT(RB_EMPTY(&anp
->an_children
), ("have children"));
543 callout_drain(&anp
->an_callout
);
545 if (anp
->an_parent
!= NULL
)
546 RB_REMOVE(autofs_node_tree
, &anp
->an_parent
->an_children
, anp
);
548 mtx_uninit(&anp
->an_vnode_lock
);
549 kfree(anp
->an_name
, M_AUTOFS
);
550 objcache_put(autofs_node_objcache
, anp
);
554 autofs_node_vn(struct autofs_node
*anp
, struct mount
*mp
, int flags
,
557 struct vnode
*vp
= NULL
;
560 KKASSERT(mtx_notlocked(&anp
->an_mount
->am_lock
));
561 mtx_lock_ex_quick(&anp
->an_vnode_lock
);
566 mtx_unlock_ex(&anp
->an_vnode_lock
);
568 error
= vget(vp
, flags
| LK_RETRY
);
570 AUTOFS_WARN("vget failed with error %d", error
);
579 mtx_unlock_ex(&anp
->an_vnode_lock
);
581 error
= getnewvnode(VT_AUTOFS
, mp
, &vp
, VLKTIMEOUT
, LK_CANRECURSE
);
587 KASSERT(anp
->an_vnode
== NULL
, ("lost race"));
589 vx_downgrade(vp
); /* downgrade VX lock to VN lock */