sys/vfs/autofs: Sync with FreeBSD's .vop_print format (FreeBSD@GitHub d8ce902a)
[dragonfly.git] / sys / vfs / autofs / autofs_vnops.c
blob9022c9bd53ef2a0c5fa3e985f2b265a825294c48
1 /*-
2 * Copyright (c) 2016 The DragonFly Project
3 * Copyright (c) 2014 The FreeBSD Foundation
4 * All rights reserved.
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
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.
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.
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/stat.h>
35 #include <sys/dirent.h>
36 #include <sys/namei.h>
37 #include <sys/nlookup.h>
38 #include <sys/mountctl.h>
40 #include "autofs.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;
47 static __inline int
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)));
59 static int
60 test_fs_root(struct vnode *vp)
62 int error;
64 if ((error = vget(vp, LK_SHARED)) != 0) {
65 AUTOFS_WARN("vget failed with error %d", error);
66 return (1);
69 if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) {
70 vput(vp);
71 return (1);
74 return (0);
77 static int
78 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp)
80 struct vnode *vp;
81 struct nlookupdata nd;
82 char *path;
83 int error;
85 path = autofs_path(anp);
87 error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
88 if (error == 0) {
89 error = nlookup(&nd);
90 if (error == 0) {
91 vp = nd.nl_nch.ncp->nc_vp;
92 error = test_fs_root(vp);
93 if (error == 0)
94 *vpp = vp;
97 nlookup_done(&nd);
98 kfree(path, M_AUTOFS);
100 return (error);
103 static int
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().
110 return (0);
113 static int
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
126 * otherwise.
127 * XXX: Not supported on DragonFly.
129 if (autofs_mount_on_stat)
130 AUTOFS_WARN("vfs.autofs.mount_on_stat is not supported");
132 vap->va_type = VDIR;
133 vap->va_mode = 0755;
134 vap->va_nlink = 3; /* XXX: FreeBSD had it like this */
135 vap->va_uid = 0;
136 vap->va_gid = 0;
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;
144 vap->va_gen = 0;
145 vap->va_flags = 0;
146 vap->va_rmajor = 0;
147 vap->va_rminor = 0;
148 vap->va_bytes = S_BLKSIZE;
149 vap->va_filerev = 0;
150 vap->va_spare = 0;
152 return (0);
155 static int
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;
161 int error;
163 KKASSERT(!vn_islocked(vp));
165 if (test_fs_root(vp) == 0)
166 goto mounted;
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)
174 goto mounted;
176 lockmgr(&autofs_softc->sc_lock, LK_EXCLUSIVE);
177 error = autofs_trigger(anp, path, pathlen);
178 lockmgr(&autofs_softc->sc_lock, LK_RELEASE);
180 if (error)
181 return (error);
183 if (nlookup_fs_root(anp, &nvp))
184 return (0);
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
190 * to trigger.
192 autofs_node_uncache(anp);
193 mounted:
194 *newvp = nvp;
195 KKASSERT(vn_islocked(*newvp));
197 return (0);
200 static int
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;
210 int error;
212 if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false &&
213 autofs_ignore_thread() == false) {
214 struct vnode *newvp = NULL;
216 cache_hold(nch);
217 cache_unlock(nch);
218 error = autofs_trigger_vn(dvp,
219 ncp->nc_name, ncp->nc_nlen, &newvp);
220 cache_lock(nch);
221 cache_drop(nch);
223 if (error)
224 return (error);
225 if (newvp != NULL) {
226 KKASSERT(newvp->v_tag != VT_AUTOFS);
227 vput(newvp);
228 return (ESTALE);
230 return (0);
233 lockmgr(&amp->am_lock, LK_SHARED);
234 error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
235 lockmgr(&amp->am_lock, LK_RELEASE);
237 if (error) {
238 cache_setvp(nch, NULL);
239 return (0);
242 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
243 if (error == 0) {
244 KKASSERT(vn_islocked(vp));
245 vn_unlock(vp);
246 cache_setvp(nch, vp);
247 vrele(vp);
248 return (0);
251 return (error);
254 static int
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;
264 int error;
267 * Do not allow mkdir() if the calling thread is not
268 * automountd(8) descendant.
270 if (autofs_ignore_thread() == false)
271 return (EPERM);
273 lockmgr(&amp->am_lock, LK_EXCLUSIVE);
274 error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
275 lockmgr(&amp->am_lock, LK_RELEASE);
276 KKASSERT(error == 0);
278 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
279 if (error == 0) {
280 KKASSERT(vn_islocked(vp));
281 cache_setunresolved(nch);
282 cache_setvp(nch, vp);
283 *(ap->a_vpp) = vp;
284 return (0);
287 return (error);
290 static int
291 autofs_readdir_one(struct uio *uio, const char *name, ino_t ino,
292 size_t *reclenp)
294 int error = 0;
296 if (reclenp != NULL)
297 *reclenp = autofs_dirent_reclen(name);
299 if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name))
300 return (EINVAL);
302 return (error);
305 static int
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;
315 int error;
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);
323 if (error)
324 return (error);
325 if (newvp != NULL) {
326 KKASSERT(newvp->v_tag != VT_AUTOFS);
327 vn_unlock(newvp);
328 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
329 ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
330 vrele(newvp);
331 return (error);
333 /* FALLTHROUGH */
336 if (uio->uio_offset < 0)
337 return (EINVAL);
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);
347 if (error)
348 goto out;
350 reclens = autofs_dirent_reclen(".");
353 * Write out the directory entry for "..".
355 if (uio->uio_offset <= reclens) {
356 if (uio->uio_offset != reclens)
357 return (EINVAL);
358 error = autofs_readdir_one(uio, "..",
359 (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino),
360 &reclen);
361 if (error)
362 goto out;
364 reclens += autofs_dirent_reclen("..");
367 * Write out the directory entries for subdirectories.
369 lockmgr(&amp->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);
377 continue;
381 * Prevent seeking into the middle of dirent.
383 if (uio->uio_offset != reclens) {
384 lockmgr(&amp->am_lock, LK_RELEASE);
385 return (EINVAL);
388 error = autofs_readdir_one(uio, child->an_name,
389 child->an_ino, &reclen);
390 reclens += reclen;
391 if (error) {
392 lockmgr(&amp->am_lock, LK_RELEASE);
393 goto out;
396 lockmgr(&amp->am_lock, LK_RELEASE);
398 if (ap->a_eofflag != NULL)
399 *ap->a_eofflag = TRUE;
401 return (0);
402 out:
404 * Return error if the initial buffer was too small to do anything.
406 if (uio->uio_resid == initial_resid)
407 return (error);
410 * Don't return an error if we managed to copy out some entries.
412 if (uio->uio_resid < reclen)
413 return (0);
415 return (error);
418 static int
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;
430 vp->v_data = NULL;
431 lockmgr(&anp->an_vnode_lock, LK_RELEASE);
433 return (0);
436 static int
437 autofs_mountctl(struct vop_mountctl_args *ap)
439 struct mount *mp;
440 #if 0
441 struct autofs_mount *amp;
442 #endif
443 int res;
445 mp = ap->a_head.a_ops->head.vv_mount;
446 lwkt_gettoken(&mp->mnt_token);
448 switch (ap->a_op) {
449 #if 0
450 case MOUNTCTL_SET_EXPORT:
451 amp = (struct autofs_mount*)mp->mnt_data;
452 if (ap->a_ctllen != sizeof(struct export_args))
453 res = (EINVAL);
454 else
455 res = vfs_export(mp, &amp->am_export,
456 (const struct export_args*)ap->a_ctl);
457 break;
458 #endif
459 default:
460 res = vop_stdmountctl(ap);
461 break;
464 lwkt_reltoken(&mp->mnt_token);
465 return (res);
468 static int
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);
476 return (0);
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,
491 #if 0
492 .vop_nlookupdotdot = NULL,
493 #endif
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);
511 if (namelen >= 0)
512 anp->an_name = kstrndup(name, namelen, M_AUTOFS);
513 else
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;
520 anp->an_mount = amp;
521 anp->an_vnode = NULL;
522 anp->an_cached = false;
523 anp->an_wildcards = false;
524 anp->an_retries = 0;
525 if (parent != NULL)
526 RB_INSERT(autofs_node_tree, &parent->an_children, anp);
527 RB_INIT(&anp->an_children);
529 *anpp = anp;
531 return (0);
535 autofs_node_find(struct autofs_node *parent, const char *name,
536 int namelen, struct autofs_node **anpp)
538 struct autofs_node *anp, find;
539 int error;
541 AUTOFS_ASSERT_LOCKED(parent->an_mount);
543 if (namelen >= 0)
544 find.an_name = kstrndup(name, namelen, M_AUTOFS);
545 else
546 find.an_name = kstrdup(name, M_AUTOFS);
548 anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
549 if (anp != NULL) {
550 error = 0;
551 if (anpp != NULL)
552 *anpp = anp;
553 } else {
554 error = ENOENT;
557 kfree(find.an_name, M_AUTOFS);
559 return (error);
562 void
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,
580 struct vnode **vpp)
582 struct vnode *vp = NULL;
583 int error;
584 retry:
585 AUTOFS_ASSERT_UNLOCKED(anp->an_mount);
586 lockmgr(&anp->an_vnode_lock, LK_EXCLUSIVE);
588 vp = anp->an_vnode;
589 if (vp != NULL) {
590 vhold(vp);
591 lockmgr(&anp->an_vnode_lock, LK_RELEASE);
593 error = vget(vp, flags | LK_RETRY);
594 if (error) {
595 AUTOFS_WARN("vget failed with error %d", error);
596 vdrop(vp);
597 goto retry;
599 vdrop(vp);
600 *vpp = vp;
601 return (0);
604 lockmgr(&anp->an_vnode_lock, LK_RELEASE);
606 error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
607 if (error)
608 return (error);
609 vp->v_type = VDIR;
610 vp->v_data = anp;
612 KASSERT(anp->an_vnode == NULL, ("lost race"));
613 anp->an_vnode = vp;
614 *vpp = vp;
616 return (0);