sys/vfs/autofs: Rename autofs_readdir_one() -> autofs_write_dirent()
[dragonfly.git] / sys / vfs / autofs / autofs_vnops.c
1 /*-
2  * Copyright (c) 2016 Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
3  * Copyright (c) 2016 The DragonFly Project
4  * Copyright (c) 2014 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Edward Tomasz Napierala under sponsorship
8  * from the FreeBSD Foundation.
9  *
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.
18  *
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.
30  *
31  */
32
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/stat.h>
36 #include <sys/dirent.h>
37 #include <sys/namei.h>
38 #include <sys/nlookup.h>
39 #include <sys/mountctl.h>
40
41 #include "autofs.h"
42
43 static bool
44 test_fs_root(struct vnode *vp)
45 {
46         int error;
47
48         if ((error = vget(vp, LK_SHARED)) != 0) {
49                 AUTOFS_WARN("vget failed with error %d", error);
50                 return (false);
51         }
52
53         if (((vp->v_flag & VROOT) == 0) || (vp->v_tag == VT_AUTOFS)) {
54                 vput(vp);
55                 return (false);
56         }
57
58         return (true);
59 }
60
61 static bool
62 nlookup_fs_root(struct autofs_node *anp, struct vnode **vpp)
63 {
64         struct nlookupdata nd;
65         char *path;
66         int error;
67
68         path = autofs_path(anp);
69
70         error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
71         if (error == 0) {
72                 error = nlookup(&nd);
73                 if (error == 0) {
74                         struct vnode *vp = nd.nl_nch.ncp->nc_vp;
75                         if (test_fs_root(vp) == true)
76                                 *vpp = vp;
77                         else
78                                 error = 1;
79                 }
80         }
81         nlookup_done(&nd);
82         kfree(path, M_AUTOFS);
83
84         return (error ? false : true);
85 }
86
87 static int
88 autofs_access(struct vop_access_args *ap)
89 {
90         /*
91          * Nothing to do here; the only kind of access control
92          * needed is in autofs_mkdir().
93          */
94         return (0);
95 }
96
97 static int
98 autofs_getattr(struct vop_getattr_args *ap)
99 {
100         struct vnode *vp = ap->a_vp;
101         struct vattr *vap = ap->a_vap;
102         struct autofs_node *anp = VTOI(vp);
103
104         KASSERT(vp->v_type == VDIR, ("!VDIR"));
105
106         /*
107          * The reason we must do this is that some tree-walking software,
108          * namely fts(3), assumes that stat(".") results will not change
109          * between chdir("subdir") and chdir(".."), and fails with ENOENT
110          * otherwise.
111          *
112          * XXX: Not supported on DragonFly.
113          * With the current trigger mechanism on DragonFly, the process
114          * will hang while in nlookup() in nlookup_fs_root().
115          */
116 #if 0
117         if (autofs_mount_on_stat && ...) {
118         }
119 #endif
120         vap->va_type = VDIR;
121         vap->va_nlink = 3; /* XXX: FreeBSD had it like this */
122         vap->va_mode = 0755;
123         vap->va_uid = 0;
124         vap->va_gid = 0;
125         vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
126         vap->va_fileid = anp->an_ino;
127         vap->va_size = S_BLKSIZE;
128         vap->va_blocksize = S_BLKSIZE;
129         vap->va_atime = anp->an_ctime;
130         vap->va_mtime = anp->an_ctime;
131         vap->va_ctime = anp->an_ctime;
132         vap->va_gen = 0;
133         vap->va_flags = 0;
134         vap->va_rmajor = 0;
135         vap->va_rminor = 0;
136         vap->va_bytes = S_BLKSIZE;
137         vap->va_filerev = 0;
138         vap->va_vaflags = 0;
139         vap->va_spare = 0;
140
141         return (0);
142 }
143
144 static int
145 autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
146     struct vnode **newvp)
147 {
148         struct autofs_node *anp = VTOI(vp);
149         struct vnode *nvp = NULL;
150         int error;
151
152         KKASSERT(!vn_islocked(vp));
153
154         if (test_fs_root(vp) == true)
155                 goto mounted;
156
157         /*
158          * Don't remove this lookup.  Without this, the process
159          * may trigger automountd(8) to mount the filesystem for the
160          * second time after successful mount, and the second attempt
161          * will fail.
162          */
163         if (nlookup_fs_root(anp, &nvp) == true)
164                 goto mounted;
165
166         mtx_lock_ex_quick(&autofs_softc->sc_lock);
167         error = autofs_trigger(anp, path, pathlen);
168         mtx_unlock_ex(&autofs_softc->sc_lock);
169
170         if (error)
171                 return (error);
172
173         if (nlookup_fs_root(anp, &nvp) == false)
174                 return (0);
175
176         /*
177          * If the operation that succeeded was mount, then mark
178          * the node as non-cached.  Otherwise, if someone unmounts
179          * the filesystem before the cache times out, we will fail
180          * to trigger.
181          */
182         autofs_node_uncache(anp);
183 mounted:
184         *newvp = nvp;
185         KKASSERT(vn_islocked(*newvp));
186
187         return (0);
188 }
189
190 static int
191 autofs_nresolve(struct vop_nresolve_args *ap)
192 {
193         struct vnode *vp = NULL;
194         struct vnode *dvp = ap->a_dvp;
195         struct nchandle *nch = ap->a_nch;
196         struct namecache *ncp = nch->ncp;
197         struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
198         struct autofs_node *anp = VTOI(dvp);
199         struct autofs_node *child = NULL;
200         int error;
201
202         if (autofs_cached(anp, ncp->nc_name, ncp->nc_nlen) == false &&
203             autofs_ignore_thread() == false) {
204                 struct vnode *newvp = NULL;
205
206                 cache_hold(nch);
207                 cache_unlock(nch);
208                 error = autofs_trigger_vn(dvp, ncp->nc_name, ncp->nc_nlen,
209                     &newvp);
210                 cache_lock(nch);
211                 cache_drop(nch);
212                 if (error)
213                         return (error);
214                 if (newvp != NULL) {
215                         KKASSERT(newvp->v_tag != VT_AUTOFS);
216                         vput(newvp);
217                         return (ESTALE);
218                 }
219                 return (0);
220         }
221
222         mtx_lock_sh_quick(&amp->am_lock);
223         error = autofs_node_find(anp, ncp->nc_name, ncp->nc_nlen, &child);
224         mtx_unlock_sh(&amp->am_lock);
225
226         if (error) {
227                 cache_setvp(nch, NULL);
228                 return (0);
229         }
230
231         error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
232         if (error == 0) {
233                 KKASSERT(vn_islocked(vp));
234                 vn_unlock(vp);
235                 cache_setvp(nch, vp);
236                 vrele(vp);
237                 return (0);
238         }
239
240         return (error);
241 }
242
243 static int
244 autofs_nmkdir(struct vop_nmkdir_args *ap)
245 {
246         struct vnode *vp = NULL;
247         struct vnode *dvp = ap->a_dvp;
248         struct nchandle *nch = ap->a_nch;
249         struct namecache *ncp = nch->ncp;
250         struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount);
251         struct autofs_node *anp = VTOI(dvp);
252         struct autofs_node *child = NULL;
253         int error;
254
255         /*
256          * Do not allow mkdir() if the calling thread is not
257          * automountd(8) descendant.
258          */
259         if (autofs_ignore_thread() == false)
260                 return (EPERM);
261
262         mtx_lock_ex_quick(&amp->am_lock);
263         error = autofs_node_new(anp, amp, ncp->nc_name, ncp->nc_nlen, &child);
264         mtx_unlock_ex(&amp->am_lock);
265         KKASSERT(error == 0);
266
267         error = autofs_node_vn(child, dvp->v_mount, LK_EXCLUSIVE, &vp);
268         if (error == 0) {
269                 KKASSERT(vn_islocked(vp));
270                 cache_setunresolved(nch);
271                 cache_setvp(nch, vp);
272                 *(ap->a_vpp) = vp;
273                 return (0);
274         }
275
276         return (error);
277 }
278
279 static __inline size_t
280 autofs_dirent_reclen(const char *name)
281 {
282         return (_DIRENT_RECLEN(strlen(name)));
283 }
284
285 static int
286 autofs_write_dirent(struct uio *uio, const char *name, ino_t ino)
287 {
288         int error = 0;
289
290         if (vop_write_dirent(&error, uio, ino, DT_DIR, strlen(name), name))
291                 return (EINVAL);
292
293         return (error);
294 }
295
296 static int
297 autofs_readdir(struct vop_readdir_args *ap)
298 {
299         struct vnode *vp = ap->a_vp;
300         struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount);
301         struct autofs_node *anp = VTOI(vp);
302         struct autofs_node *child;
303         struct uio *uio = ap->a_uio;
304         ssize_t initial_resid = ap->a_uio->uio_resid;
305         size_t reclens = 0;
306         int error;
307
308         KASSERT(vp->v_type == VDIR, ("!VDIR"));
309
310         if (autofs_cached(anp, NULL, 0) == false &&
311             autofs_ignore_thread() == false) {
312                 struct vnode *newvp = NULL;
313                 error = autofs_trigger_vn(vp, "", 0, &newvp);
314                 if (error)
315                         return (error);
316                 if (newvp != NULL) {
317                         KKASSERT(newvp->v_tag != VT_AUTOFS);
318                         vn_unlock(newvp);
319                         error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
320                             ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
321                         vrele(newvp);
322                         return (error);
323                 }
324         }
325
326         if (uio->uio_offset < 0)
327                 return (EINVAL);
328
329         if (ap->a_eofflag != NULL)
330                 *ap->a_eofflag = FALSE;
331
332         /*
333          * Write out the directory entry for ".".
334          */
335         if (uio->uio_offset == 0) {
336                 error = autofs_write_dirent(uio, ".", anp->an_ino);
337                 if (error)
338                         goto out;
339         }
340         reclens = autofs_dirent_reclen(".");
341
342         /*
343          * Write out the directory entry for "..".
344          */
345         if (uio->uio_offset <= reclens) {
346                 if (uio->uio_offset != reclens)
347                         return (EINVAL);
348                 error = autofs_write_dirent(uio, "..",
349                     (anp->an_parent ? anp->an_parent->an_ino : anp->an_ino));
350                 if (error)
351                         goto out;
352         }
353         reclens += autofs_dirent_reclen("..");
354
355         /*
356          * Write out the directory entries for subdirectories.
357          */
358         mtx_lock_sh_quick(&amp->am_lock);
359         RB_FOREACH(child, autofs_node_tree, &anp->an_children) {
360                 /*
361                  * Check the offset to skip entries returned by previous
362                  * calls to getdents().
363                  */
364                 if (uio->uio_offset > reclens) {
365                         reclens += autofs_dirent_reclen(child->an_name);
366                         continue;
367                 }
368
369                 /*
370                  * Prevent seeking into the middle of dirent.
371                  */
372                 if (uio->uio_offset != reclens) {
373                         mtx_unlock_sh(&amp->am_lock);
374                         return (EINVAL);
375                 }
376
377                 error = autofs_write_dirent(uio, child->an_name, child->an_ino);
378                 reclens += autofs_dirent_reclen(child->an_name);
379                 if (error) {
380                         mtx_unlock_sh(&amp->am_lock);
381                         goto out;
382                 }
383         }
384         mtx_unlock_sh(&amp->am_lock);
385
386         if (ap->a_eofflag != NULL)
387                 *ap->a_eofflag = TRUE;
388
389         return (0);
390 out:
391         /*
392          * Return error if the initial buffer was too small to do anything.
393          */
394         if (uio->uio_resid == initial_resid)
395                 return (error);
396
397         /*
398          * Don't return an error if we managed to copy out some entries.
399          */
400         if (uio->uio_resid < initial_resid)
401                 return (0);
402
403         return (error);
404 }
405
406 static int
407 autofs_reclaim(struct vop_reclaim_args *ap)
408 {
409         struct vnode *vp = ap->a_vp;
410         struct autofs_node *anp = VTOI(vp);
411
412         /*
413          * We do not free autofs_node here; instead we are
414          * destroying them in autofs_node_delete().
415          */
416         mtx_lock_ex_quick(&anp->an_vnode_lock);
417         anp->an_vnode = NULL;
418         vp->v_data = NULL;
419         mtx_unlock_ex(&anp->an_vnode_lock);
420
421         return (0);
422 }
423
424 static int
425 autofs_mountctl(struct vop_mountctl_args *ap)
426 {
427         struct mount *mp;
428         int res = 0;
429
430         mp = ap->a_head.a_ops->head.vv_mount;
431         lwkt_gettoken(&mp->mnt_token);
432
433         switch (ap->a_op) {
434         //case ...:
435         //      break;
436         default:
437                 res = vop_stdmountctl(ap);
438                 break;
439         }
440
441         lwkt_reltoken(&mp->mnt_token);
442         return (res);
443 }
444
445 static int
446 autofs_print(struct vop_print_args *ap)
447 {
448         struct autofs_node *anp = VTOI(ap->a_vp);
449
450         kprintf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, retries %d, wildcards %d\n",
451             anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached,
452             anp->an_retries, anp->an_wildcards);
453
454         return (0);
455 }
456
457 struct vop_ops autofs_vnode_vops = {
458         .vop_default =          vop_defaultop,
459         .vop_getpages =         vop_stdgetpages,
460         .vop_putpages =         vop_stdputpages,
461         .vop_access =           autofs_access,
462         .vop_getattr =          autofs_getattr,
463         .vop_nresolve =         autofs_nresolve,
464         .vop_nmkdir =           autofs_nmkdir,
465         .vop_readdir =          autofs_readdir,
466         .vop_reclaim =          autofs_reclaim,
467         .vop_mountctl =         autofs_mountctl,
468         .vop_print =            autofs_print,
469 };
470
471 int
472 autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
473     const char *name, int namelen, struct autofs_node **anpp)
474 {
475         struct autofs_node *anp;
476
477         KKASSERT(mtx_islocked_ex(&amp->am_lock));
478
479         if (parent != NULL) {
480                 KKASSERT(mtx_islocked_ex(&parent->an_mount->am_lock));
481                 KASSERT(autofs_node_find(parent, name, namelen, NULL) == ENOENT,
482                     ("node \"%s\" already exists", name));
483         }
484
485         /*
486          * All struct fields must be initialized.
487          */
488         anp = objcache_get(autofs_node_objcache, M_WAITOK);
489         if (namelen >= 0)
490                 anp->an_name = kstrndup(name, namelen, M_AUTOFS);
491         else
492                 anp->an_name = kstrdup(name, M_AUTOFS);
493         anp->an_ino = amp->am_last_ino++;
494         callout_init(&anp->an_callout);
495         mtx_init(&anp->an_vnode_lock, "autofsvnlk");
496         getnanotime(&anp->an_ctime);
497         anp->an_parent = parent;
498         anp->an_mount = amp;
499         anp->an_vnode = NULL;
500         anp->an_cached = false;
501         anp->an_wildcards = false;
502         anp->an_retries = 0;
503         if (parent != NULL)
504                 RB_INSERT(autofs_node_tree, &parent->an_children, anp);
505         RB_INIT(&anp->an_children);
506
507         *anpp = anp;
508
509         return (0);
510 }
511
512 int
513 autofs_node_find(struct autofs_node *parent, const char *name, int namelen,
514     struct autofs_node **anpp)
515 {
516         struct autofs_node *anp, find;
517         int error;
518
519         KKASSERT(mtx_islocked(&parent->an_mount->am_lock));
520
521         if (namelen >= 0)
522                 find.an_name = kstrndup(name, namelen, M_AUTOFS);
523         else
524                 find.an_name = kstrdup(name, M_AUTOFS);
525
526         anp = RB_FIND(autofs_node_tree, &parent->an_children, &find);
527         if (anp != NULL) {
528                 error = 0;
529                 if (anpp != NULL)
530                         *anpp = anp;
531         } else {
532                 error = ENOENT;
533         }
534
535         kfree(find.an_name, M_AUTOFS);
536
537         return (error);
538 }
539
540 void
541 autofs_node_delete(struct autofs_node *anp)
542 {
543         KKASSERT(mtx_islocked_ex(&anp->an_mount->am_lock));
544         KASSERT(RB_EMPTY(&anp->an_children), ("have children"));
545
546         callout_drain(&anp->an_callout);
547
548         if (anp->an_parent != NULL)
549                 RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp);
550
551         mtx_uninit(&anp->an_vnode_lock);
552         kfree(anp->an_name, M_AUTOFS);
553         objcache_put(autofs_node_objcache, anp);
554 }
555
556 int
557 autofs_node_vn(struct autofs_node *anp, struct mount *mp, int flags,
558     struct vnode **vpp)
559 {
560         struct vnode *vp = NULL;
561         int error;
562 retry:
563         KKASSERT(mtx_notlocked(&anp->an_mount->am_lock));
564         mtx_lock_ex_quick(&anp->an_vnode_lock);
565
566         vp = anp->an_vnode;
567         if (vp != NULL) {
568                 vhold(vp);
569                 mtx_unlock_ex(&anp->an_vnode_lock);
570
571                 error = vget(vp, flags | LK_RETRY);
572                 if (error) {
573                         AUTOFS_WARN("vget failed with error %d", error);
574                         vdrop(vp);
575                         goto retry;
576                 }
577                 vdrop(vp);
578                 *vpp = vp;
579                 return (0);
580         }
581
582         mtx_unlock_ex(&anp->an_vnode_lock);
583
584         error = getnewvnode(VT_AUTOFS, mp, &vp, VLKTIMEOUT, LK_CANRECURSE);
585         if (error)
586                 return (error);
587         vp->v_type = VDIR;
588         vp->v_data = anp;
589
590         KASSERT(anp->an_vnode == NULL, ("lost race"));
591         anp->an_vnode = vp;
592         *vpp = vp;
593
594         return (0);
595 }