kernel: Remove <sys/n{amei,lookup}.h> from all files that don't need it.
[dragonfly.git] / sys / vfs / autofs / autofs_vnops.c
blob7a7be5a8ee032fc60a27b7f3864233fe4fd1ee2d
1 /*-
2 * Copyright (c) 2016 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2016 The DragonFly Project
4 * Copyright (c) 2014 The FreeBSD Foundation
5 * All rights reserved.
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
12 * are met:
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
29 * SUCH DAMAGE.
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/stat.h>
36 #include <sys/dirent.h>
37 #include <sys/nlookup.h>
38 #include <sys/mountctl.h>
40 #include "autofs.h"
42 static bool
43 test_fs_root(struct vnode *vp)
45 int error;
47 if ((error = vget(vp, LK_SHARED)) != 0) {
48 AUTOFS_WARN("vget failed with error %d", error);
49 return (false);
52 if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) {
53 vput(vp);
54 return (false);
57 return (true);
60 static bool
61 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp)
63 struct nlookupdata nd;
64 char *path;
65 int error;
67 path = autofs_path(anp);
69 error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
70 if (error == 0) {
71 error = nlookup(&nd);
72 if (error == 0) {
73 struct vnode *vp = nd.nl_nch.ncp->nc_vp;
74 if (test_fs_root(vp) == true)
75 *vpp = vp;
76 else
77 error = 1;
80 nlookup_done(&nd);
81 kfree(path, M_AUTOFS);
83 return (error ? false : true);
86 static int
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().
93 return (0);
96 static int
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
109 * otherwise.
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().
115 #if 0
116 if (autofs_mount_on_stat && ...) {
118 #endif
119 vap->va_type = VDIR;
120 vap->va_nlink = 3; /* XXX: FreeBSD had it like this */
121 vap->va_mode = 0755;
122 vap->va_uid = 0;
123 vap->va_gid = 0;
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;
131 vap->va_gen = 0;
132 vap->va_flags = 0;
133 vap->va_rmajor = 0;
134 vap->va_rminor = 0;
135 vap->va_bytes = S_BLKSIZE;
136 vap->va_filerev = 0;
137 vap->va_vaflags = 0;
138 vap->va_spare = 0;
140 return (0);
143 static int
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;
149 int error;
151 KKASSERT(!vn_islocked(vp));
153 if (test_fs_root(vp) == true)
154 goto mounted;
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
160 * will fail.
162 if (nlookup_fs_root(anp, &nvp) == true)
163 goto mounted;
165 mtx_lock_ex_quick(&autofs_softc->sc_lock);
166 error = autofs_trigger(anp, path, pathlen);
167 mtx_unlock_ex(&autofs_softc->sc_lock);
169 if (error)
170 return (error);
172 if (nlookup_fs_root(anp, &nvp) == false)
173 return (0);
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
179 * to trigger.
181 autofs_node_uncache(anp);
182 mounted:
183 *newvp = nvp;
184 KKASSERT(vn_islocked(*newvp));
186 return (0);
189 static int
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;
199 int error;
201 if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false &&
202 autofs_ignore_thread() == false) {
203 struct vnode *newvp = NULL;
205 cache_hold(nch);
206 cache_unlock(nch);
207 error = autofs_trigger_vn(dvp, ncp->nc_name, ncp->nc_nlen,
208 &newvp);
209 cache_lock(nch);
210 cache_drop(nch);
211 if (error)
212 return (error);
213 if (newvp != NULL) {
214 KKASSERT(newvp->v_tag != VT_AUTOFS);
215 vput(newvp);
216 return (ESTALE);
218 return (0);
221 mtx_lock_sh_quick(&amp->am_lock);
222 error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
223 mtx_unlock_sh(&amp->am_lock);
225 if (error) {
226 cache_setvp(nch, NULL);
227 return (0);
230 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
231 if (error == 0) {
232 KKASSERT(vn_islocked(vp));
233 vn_unlock(vp);
234 cache_setvp(nch, vp);
235 vrele(vp);
236 return (0);
239 return (error);
242 static int
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;
252 int error;
255 * Do not allow mkdir() if the calling thread is not
256 * automountd(8) descendant.
258 if (autofs_ignore_thread() == false)
259 return (EPERM);
261 mtx_lock_ex_quick(&amp->am_lock);
262 error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
263 mtx_unlock_ex(&amp->am_lock);
264 KKASSERT(error == 0);
266 error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
267 if (error == 0) {
268 KKASSERT(vn_islocked(vp));
269 cache_setunresolved(nch);
270 cache_setvp(nch, vp);
271 *(ap->a_vpp) = vp;
272 return (0);
275 return (error);
278 static __inline size_t
279 autofs_dirent_reclen(const char *name)
281 return (_DIRENT_RECLEN(strlen(name)));
284 static int
285 autofs_write_dirent(struct uio *uio, const char *name, ino_t ino)
287 int error = 0;
289 if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name))
290 return (EINVAL);
292 return (error);
295 static int
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;
304 size_t reclens;
305 int error;
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);
313 if (error)
314 return (error);
315 if (newvp != NULL) {
316 KKASSERT(newvp->v_tag != VT_AUTOFS);
317 vn_unlock(newvp);
318 error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
319 ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
320 vrele(newvp);
321 return (error);
325 if (uio->uio_offset < 0)
326 return (EINVAL);
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);
336 if (error)
337 goto out;
339 reclens = autofs_dirent_reclen(".");
342 * Write out the directory entry for "..".
344 if (uio->uio_offset <= reclens) {
345 if (uio->uio_offset != reclens)
346 return (EINVAL);
347 error = autofs_write_dirent(uio, "..",
348 anp->an_parent ? anp->an_parent->an_ino : anp->an_ino);
349 if (error)
350 goto out;
352 reclens += autofs_dirent_reclen("..");
355 * Write out the directory entries for subdirectories.
357 mtx_lock_sh_quick(&amp->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);
365 continue;
369 * Prevent seeking into the middle of dirent.
371 if (uio->uio_offset != reclens) {
372 mtx_unlock_sh(&amp->am_lock);
373 return (EINVAL);
376 error = autofs_write_dirent(uio, child->an_name, child->an_ino);
377 reclens += autofs_dirent_reclen(child->an_name);
378 if (error) {
379 mtx_unlock_sh(&amp->am_lock);
380 goto out;
383 mtx_unlock_sh(&amp->am_lock);
385 if (ap->a_eofflag != NULL)
386 *ap->a_eofflag = TRUE;
388 return (0);
389 out:
391 * Return error if the initial buffer was too small to do anything.
393 if (uio->uio_resid == initial_resid)
394 return (error);
397 * Don't return an error if we managed to copy out some entries.
399 if (uio->uio_resid < initial_resid)
400 return (0);
402 return (error);
405 static int
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;
417 vp->v_data = NULL;
418 mtx_unlock_ex(&anp->an_vnode_lock);
420 return (0);
423 static int
424 autofs_mountctl(struct vop_mountctl_args *ap)
426 struct mount *mp;
427 int res = 0;
429 mp = ap->a_head.a_ops->head.vv_mount;
430 lwkt_gettoken(&mp->mnt_token);
432 switch (ap->a_op) {
433 //case ...:
434 // break;
435 default:
436 res = vop_stdmountctl(ap);
437 break;
440 lwkt_reltoken(&mp->mnt_token);
441 return (res);
444 static int
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);
453 return (0);
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(&amp->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);
486 if (namelen >= 0)
487 anp->an_name = kstrndup(name, namelen, M_AUTOFS);
488 else
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;
495 anp->an_mount = amp;
496 anp->an_vnode = NULL;
497 anp->an_cached = false;
498 anp->an_wildcards = false;
499 anp->an_retries = 0;
500 if (parent != NULL)
501 RB_INSERT(autofs_node_tree, &parent->an_children, anp);
502 RB_INIT(&anp->an_children);
504 *anpp = anp;
506 return (0);
510 autofs_node_find(struct autofs_node *parent, const char *name, int namelen,
511 struct autofs_node **anpp)
513 struct autofs_node *anp, find;
514 int error;
516 KKASSERT(mtx_islocked(&parent->an_mount->am_lock));
518 if (namelen >= 0)
519 find.an_name = kstrndup(name, namelen, M_AUTOFS);
520 else
521 find.an_name = kstrdup(name, M_AUTOFS);
523 anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
524 if (anp != NULL) {
525 error = 0;
526 if (anpp != NULL)
527 *anpp = anp;
528 } else {
529 error = ENOENT;
532 kfree(find.an_name, M_AUTOFS);
534 return (error);
537 void
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,
555 struct vnode **vpp)
557 struct vnode *vp = NULL;
558 int error;
559 retry:
560 KKASSERT(mtx_notlocked(&anp->an_mount->am_lock));
561 mtx_lock_ex_quick(&anp->an_vnode_lock);
563 vp = anp->an_vnode;
564 if (vp != NULL) {
565 vhold(vp);
566 mtx_unlock_ex(&anp->an_vnode_lock);
568 error = vget(vp, flags | LK_RETRY);
569 if (error) {
570 AUTOFS_WARN("vget failed with error %d", error);
571 vdrop(vp);
572 goto retry;
574 vdrop(vp);
575 *vpp = vp;
576 return (0);
579 mtx_unlock_ex(&anp->an_vnode_lock);
581 error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
582 if (error)
583 return (error);
584 vp->v_type = VDIR;
585 vp->v_data = anp;
587 KASSERT(anp->an_vnode == NULL, ("lost race"));
588 anp->an_vnode = vp;
589 vx_downgrade(vp); /* downgrade VX lock to VN lock */
590 *vpp = vp;
592 return (0);