2 * Copyright (c) 2016 The DragonFly Project
3 * Copyright (c) 2014 The FreeBSD Foundation
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/kernel.h>
33 #include <sys/module.h>
35 #include <sys/dirent.h>
36 #include <sys/namei.h>
37 #include <sys/nlookup.h>
38 #include <sys/mountctl.h>
42 static int autofs_trigger_vn(struct vnode
*vp
, const char *path
,
43 int pathlen
, struct vnode
**newvp
);
45 extern struct autofs_softc
*autofs_softc
;
48 autofs_trigger_vn_dir(struct vnode
*vp
, struct vnode
**newvp
)
50 return (autofs_trigger_vn(vp
, "", 0, newvp
));
53 static __inline
size_t
54 autofs_dirent_reclen(const char *name
)
56 return (_DIRENT_RECLEN(strlen(name
)));
60 test_fs_root(struct vnode
*vp
)
64 if ((error
= vget(vp
, LK_SHARED
)) != 0) {
65 AUTOFS_WARN("vget failed with error %d", error
);
69 if (((vp
->v_flag
& VROOT
) == 0) || (vp
->v_tag
== VT_AUTOFS
)) {
78 nlookup_fs_root(struct autofs_node
*anp
, struct vnode
**vpp
)
81 struct nlookupdata nd
;
85 path
= autofs_path(anp
);
87 error
= nlookup_init(&nd
, path
, UIO_SYSSPACE
, NLC_FOLLOW
);
91 vp
= nd
.nl_nch
.ncp
->nc_vp
;
92 error
= test_fs_root(vp
);
98 kfree(path
, M_AUTOFS
);
104 autofs_access(struct vop_access_args
*ap
)
107 * Nothing to do here; the only kind of access control
108 * needed is in autofs_mkdir().
114 autofs_getattr(struct vop_getattr_args
*ap
)
116 struct vnode
*vp
= ap
->a_vp
;
117 struct vattr
*vap
= ap
->a_vap
;
118 struct autofs_node
*anp
= VTOI(vp
);
120 KASSERT(vp
->v_type
== VDIR
, ("!VDIR"));
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
127 * XXX: Not supported on DragonFly.
129 if (autofs_mount_on_stat
)
130 AUTOFS_WARN("vfs.autofs.mount_on_stat is not supported");
134 vap
->va_nlink
= 3; /* XXX: FreeBSD had it like this */
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
;
148 vap
->va_bytes
= S_BLKSIZE
;
156 autofs_trigger_vn(struct vnode
*vp
, const char *path
, int pathlen
,
157 struct vnode
**newvp
)
159 struct autofs_node
*anp
= VTOI(vp
);
160 struct vnode
*nvp
= NULL
;
163 KKASSERT(!vn_islocked(vp
));
165 if (test_fs_root(vp
) == 0)
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.
173 if (nlookup_fs_root(anp
, &nvp
) == 0)
176 lockmgr(&autofs_softc
->sc_lock
, LK_EXCLUSIVE
);
177 error
= autofs_trigger(anp
, path
, pathlen
);
178 lockmgr(&autofs_softc
->sc_lock
, LK_RELEASE
);
183 if (nlookup_fs_root(anp
, &nvp
))
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
192 autofs_node_uncache(anp
);
195 KKASSERT(vn_islocked(*newvp
));
201 autofs_nresolve(struct vop_nresolve_args
*ap
)
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
;
212 if (autofs_cached(anp
, ncp
->nc_name
, ncp
->nc_nlen
) == false &&
213 autofs_ignore_thread() == false) {
214 struct vnode
*newvp
= NULL
;
218 error
= autofs_trigger_vn(dvp
,
219 ncp
->nc_name
, ncp
->nc_nlen
, &newvp
);
226 KKASSERT(newvp
->v_tag
!= VT_AUTOFS
);
233 lockmgr(&
->am_lock
, LK_SHARED
);
234 error
= autofs_node_find(anp
, ncp
->nc_name
, ncp
->nc_nlen
, &child
);
235 lockmgr(&
->am_lock
, LK_RELEASE
);
238 cache_setvp(nch
, NULL
);
242 error
= autofs_node_vn(child
, dvp
->v_mount
, LK_EXCLUSIVE
, &vp
);
244 KKASSERT(vn_islocked(vp
));
246 cache_setvp(nch
, vp
);
255 autofs_nmkdir(struct vop_nmkdir_args
*ap
)
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
;
267 * Do not allow mkdir() if the calling thread is not
268 * automountd(8) descendant.
270 if (autofs_ignore_thread() == false)
273 lockmgr(&
->am_lock
, LK_EXCLUSIVE
);
274 error
= autofs_node_new(anp
, amp
, ncp
->nc_name
, ncp
->nc_nlen
, &child
);
275 lockmgr(&
->am_lock
, LK_RELEASE
);
276 KKASSERT(error
== 0);
278 error
= autofs_node_vn(child
, dvp
->v_mount
, LK_EXCLUSIVE
, &vp
);
280 KKASSERT(vn_islocked(vp
));
281 cache_setunresolved(nch
);
282 cache_setvp(nch
, vp
);
291 autofs_readdir_one(struct uio
*uio
, const char *name
, ino_t ino
,
297 *reclenp
= autofs_dirent_reclen(name
);
299 if (vop_write_dirent(&error
, uio
, ino
, DT_DIR
, strlen(name
), name
))
306 autofs_readdir(struct vop_readdir_args
*ap
)
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
;
317 KASSERT(vp
->v_type
== VDIR
, ("!VDIR"));
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
);
326 KKASSERT(newvp
->v_tag
!= VT_AUTOFS
);
328 error
= VOP_READDIR(newvp
, ap
->a_uio
, ap
->a_cred
,
329 ap
->a_eofflag
, ap
->a_ncookies
, ap
->a_cookies
);
336 if (uio
->uio_offset
< 0)
339 if (ap
->a_eofflag
!= NULL
)
340 *ap
->a_eofflag
= FALSE
;
343 * Write out the directory entry for ".".
345 if (uio
->uio_offset
== 0) {
346 error
= autofs_readdir_one(uio
, ".", anp
->an_ino
, &reclen
);
350 reclens
= autofs_dirent_reclen(".");
353 * Write out the directory entry for "..".
355 if (uio
->uio_offset
<= reclens
) {
356 if (uio
->uio_offset
!= reclens
)
358 error
= autofs_readdir_one(uio
, "..",
359 (anp
->an_parent
? anp
->an_parent
->an_ino
: anp
->an_ino
),
364 reclens
+= autofs_dirent_reclen("..");
367 * Write out the directory entries for subdirectories.
369 lockmgr(&
->am_lock
, LK_SHARED
);
370 RB_FOREACH(child
, autofs_node_tree
, &anp
->an_children
) {
372 * Check the offset to skip entries returned by previous
373 * calls to getdents().
375 if (uio
->uio_offset
> reclens
) {
376 reclens
+= autofs_dirent_reclen(child
->an_name
);
381 * Prevent seeking into the middle of dirent.
383 if (uio
->uio_offset
!= reclens
) {
384 lockmgr(&
->am_lock
, LK_RELEASE
);
388 error
= autofs_readdir_one(uio
, child
->an_name
,
389 child
->an_ino
, &reclen
);
392 lockmgr(&
->am_lock
, LK_RELEASE
);
396 lockmgr(&
->am_lock
, LK_RELEASE
);
398 if (ap
->a_eofflag
!= NULL
)
399 *ap
->a_eofflag
= TRUE
;
404 * Return error if the initial buffer was too small to do anything.
406 if (uio
->uio_resid
== initial_resid
)
410 * Don't return an error if we managed to copy out some entries.
412 if (uio
->uio_resid
< reclen
)
419 autofs_reclaim(struct vop_reclaim_args
*ap
)
421 struct vnode
*vp
= ap
->a_vp
;
422 struct autofs_node
*anp
= VTOI(vp
);
425 * We do not free autofs_node here; instead we are
426 * destroying them in autofs_node_delete().
428 lockmgr(&anp
->an_vnode_lock
, LK_EXCLUSIVE
);
429 anp
->an_vnode
= NULL
;
431 lockmgr(&anp
->an_vnode_lock
, LK_RELEASE
);
437 autofs_mountctl(struct vop_mountctl_args
*ap
)
441 struct autofs_mount
*amp
;
445 mp
= ap
->a_head
.a_ops
->head
.vv_mount
;
446 lwkt_gettoken(&mp
->mnt_token
);
450 case MOUNTCTL_SET_EXPORT
:
451 amp
= (struct autofs_mount
*)mp
->mnt_data
;
452 if (ap
->a_ctllen
!= sizeof(struct export_args
))
455 res
= vfs_export(mp
, &
->am_export
,
456 (const struct export_args
*)ap
->a_ctl
);
460 res
= vop_stdmountctl(ap
);
464 lwkt_reltoken(&mp
->mnt_token
);
469 autofs_print(struct vop_print_args
*ap
)
471 struct autofs_node
*anp
= VTOI(ap
->a_vp
);
473 kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
474 anp
, (intmax_t)anp
->an_ino
, anp
->an_name
, anp
->an_cached
, anp
->an_retries
, anp
->an_wildcards
);
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
,
492 .vop_nlookupdotdot
= NULL
,
497 autofs_node_new(struct autofs_node
*parent
, struct autofs_mount
*amp
,
498 const char *name
, int namelen
, struct autofs_node
**anpp
)
500 struct autofs_node
*anp
;
502 AUTOFS_ASSERT_XLOCKED(amp
);
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
));
510 anp
= objcache_get(autofs_node_objcache
, M_WAITOK
);
512 anp
->an_name
= kstrndup(name
, namelen
, M_AUTOFS
);
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
;
521 anp
->an_vnode
= NULL
;
522 anp
->an_cached
= false;
523 anp
->an_wildcards
= false;
526 RB_INSERT(autofs_node_tree
, &parent
->an_children
, anp
);
527 RB_INIT(&anp
->an_children
);
535 autofs_node_find(struct autofs_node
*parent
, const char *name
,
536 int namelen
, struct autofs_node
**anpp
)
538 struct autofs_node
*anp
, find
;
541 AUTOFS_ASSERT_LOCKED(parent
->an_mount
);
544 find
.an_name
= kstrndup(name
, namelen
, M_AUTOFS
);
546 find
.an_name
= kstrdup(name
, M_AUTOFS
);
548 anp
= RB_FIND(autofs_node_tree
, &parent
->an_children
, &find
);
557 kfree(find
.an_name
, M_AUTOFS
);
563 autofs_node_delete(struct autofs_node
*anp
)
565 AUTOFS_ASSERT_XLOCKED(anp
->an_mount
);
566 KASSERT(RB_EMPTY(&anp
->an_children
), ("have children"));
568 callout_drain(&anp
->an_callout
);
570 if (anp
->an_parent
!= NULL
)
571 RB_REMOVE(autofs_node_tree
, &anp
->an_parent
->an_children
, anp
);
573 lockuninit(&anp
->an_vnode_lock
);
574 kfree(anp
->an_name
, M_AUTOFS
);
575 objcache_put(autofs_node_objcache
, anp
);
579 autofs_node_vn(struct autofs_node
*anp
, struct mount
*mp
, int flags
,
582 struct vnode
*vp
= NULL
;
585 AUTOFS_ASSERT_UNLOCKED(anp
->an_mount
);
586 lockmgr(&anp
->an_vnode_lock
, LK_EXCLUSIVE
);
591 lockmgr(&anp
->an_vnode_lock
, LK_RELEASE
);
593 error
= vget(vp
, flags
| LK_RETRY
);
595 AUTOFS_WARN("vget failed with error %d", error
);
604 lockmgr(&anp
->an_vnode_lock
, LK_RELEASE
);
606 error
= getnewvnode(VT_AUTOFS
, mp
, &vp
, VLKTIMEOUT
, LK_CANRECURSE
);
612 KASSERT(anp
->an_vnode
== NULL
, ("lost race"));