cf8e0be77040446a9db05b60fdac9e26d076a470
[dragonfly.git] / sys / vfs / autofs / autofs_vfsops.c
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.
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
37 #include "autofs.h"
38 #include "autofs_mount.h"
39
40 static int      autofs_statfs(struct mount *mp, struct statfs *sbp,
41                     struct ucred *cred);
42
43 static struct objcache_malloc_args autofs_request_args = {
44         sizeof(struct autofs_request), M_AUTOFS,
45 };
46 static struct objcache_malloc_args autofs_node_args = {
47         sizeof(struct autofs_node), M_AUTOFS,
48 };
49
50 static int
51 autofs_init(struct vfsconf *vfsp)
52 {
53         KASSERT(autofs_softc == NULL,
54             ("softc %p, should be NULL", autofs_softc));
55
56         autofs_softc = kmalloc(sizeof(*autofs_softc), M_AUTOFS,
57             M_WAITOK | M_ZERO);
58
59         autofs_request_objcache = objcache_create("autofs_request", 0, 0,
60                 NULL, NULL, NULL,
61                 objcache_malloc_alloc_zero, objcache_malloc_free,
62                 &autofs_request_args);
63
64         autofs_node_objcache = objcache_create("autofs_node", 0, 0,
65                 NULL, NULL, NULL,
66                 objcache_malloc_alloc_zero, objcache_malloc_free,
67                 &autofs_node_args);
68
69         TAILQ_INIT(&autofs_softc->sc_requests);
70         cv_init(&autofs_softc->sc_cv, "autofscv");
71         mtx_init(&autofs_softc->sc_lock, "autofssclk");
72         autofs_softc->sc_dev_opened = false;
73
74         autofs_softc->sc_cdev = make_dev(&autofs_ops, 0, UID_ROOT, GID_OPERATOR,
75             0640, "autofs");
76         if (autofs_softc->sc_cdev == NULL) {
77                 AUTOFS_WARN("failed to create device node");
78                 objcache_destroy(autofs_request_objcache);
79                 objcache_destroy(autofs_node_objcache);
80                 kfree(autofs_softc, M_AUTOFS);
81                 return (ENODEV);
82         }
83         autofs_softc->sc_cdev->si_drv1 = autofs_softc;
84
85         return (0);
86 }
87
88 static int
89 autofs_uninit(struct vfsconf *vfsp)
90 {
91         mtx_lock_ex_quick(&autofs_softc->sc_lock);
92         if (autofs_softc->sc_dev_opened) {
93                 mtx_unlock_ex(&autofs_softc->sc_lock);
94                 return (EBUSY);
95         }
96
97         if (autofs_softc->sc_cdev != NULL)
98                 destroy_dev(autofs_softc->sc_cdev);
99
100         objcache_destroy(autofs_request_objcache);
101         objcache_destroy(autofs_node_objcache);
102
103         mtx_unlock_ex(&autofs_softc->sc_lock);
104
105         kfree(autofs_softc, M_AUTOFS); /* race with open */
106         autofs_softc = NULL;
107
108         return (0);
109 }
110
111 static int
112 autofs_mount(struct mount *mp, char *mntpt, caddr_t data, struct ucred *cred)
113 {
114         struct autofs_mount_info info;
115         struct autofs_mount *amp;
116         struct statfs *sbp = &mp->mnt_stat;
117         int error;
118
119         if (mp->mnt_flag & MNT_UPDATE) {
120                 autofs_flush(VFSTOAUTOFS(mp));
121                 return (0);
122         }
123
124         error = copyin(data, &info, sizeof(info));
125         if (error)
126                 return (error);
127
128         /*
129          * Copy-in ->f_mntfromname string.
130          */
131         memset(sbp->f_mntfromname, 0, sizeof(sbp->f_mntfromname));
132         error = copyinstr(info.from, sbp->f_mntfromname,
133             sizeof(sbp->f_mntfromname), NULL);
134         if (error)
135                 return (error);
136         /*
137          * Copy-in ->f_mntonname string.
138          */
139         memset(sbp->f_mntonname, 0, sizeof(sbp->f_mntonname));
140         error = copyinstr(mntpt, sbp->f_mntonname, sizeof(sbp->f_mntonname),
141             NULL);
142         if (error)
143                 return (error);
144
145         /*
146          * Allocate the autofs mount.
147          */
148         amp = kmalloc(sizeof(*amp), M_AUTOFS, M_WAITOK | M_ZERO);
149         mp->mnt_data = (qaddr_t)amp;
150         strlcpy(amp->am_from, sbp->f_mntfromname, sizeof(amp->am_from));
151         strlcpy(amp->am_on, sbp->f_mntonname, sizeof(amp->am_on));
152
153         /*
154          * Copy-in master_options string.
155          */
156         error = copyinstr(info.master_options, amp->am_options,
157             sizeof(amp->am_options), NULL);
158         if (error)
159                 goto fail;
160         /*
161          * Copy-in master_prefix string.
162          */
163         error = copyinstr(info.master_prefix, amp->am_prefix,
164             sizeof(amp->am_prefix), NULL);
165         if (error)
166                 goto fail;
167
168         /*
169          * Initialize the autofs mount.
170          */
171         mtx_init(&amp->am_lock, "autofsmnlk");
172         amp->am_last_ino = AUTOFS_ROOTINO;
173
174         mtx_lock_ex_quick(&amp->am_lock);
175         error = autofs_node_new(NULL, amp, ".", -1, &amp->am_root);
176         mtx_unlock_ex(&amp->am_lock);
177         KKASSERT(error == 0);
178         KKASSERT(amp->am_root->an_ino == AUTOFS_ROOTINO);
179
180         vfs_getnewfsid(mp);
181         vfs_add_vnodeops(mp, &autofs_vnode_vops, &mp->mnt_vn_norm_ops);
182
183         VFS_STATFS(mp, &mp->mnt_stat, cred);
184
185         return (0);
186
187 fail:
188         kfree(amp, M_AUTOFS);
189         return (error);
190 }
191
192 static int
193 autofs_unmount(struct mount *mp, int mntflags)
194 {
195         struct autofs_mount *amp = VFSTOAUTOFS(mp);
196         int error, flags;
197
198         flags = 0;
199         if (mntflags & MNT_FORCE)
200                 flags |= FORCECLOSE;
201         error = vflush(mp, 0, flags);
202         if (error) {
203                 AUTOFS_WARN("vflush failed with error %d", error);
204                 return (error);
205         }
206
207         /*
208          * All vnodes are gone, and new one will not appear - so,
209          * no new triggerings.
210          */
211         for (;;) {
212                 struct autofs_request *ar;
213                 int dummy;
214                 bool found;
215
216                 found = false;
217                 mtx_lock_ex_quick(&autofs_softc->sc_lock);
218                 TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) {
219                         if (ar->ar_mount != amp)
220                                 continue;
221                         ar->ar_error = ENXIO;
222                         ar->ar_done = true;
223                         ar->ar_in_progress = false;
224                         found = true;
225                 }
226                 if (found == false) {
227                         mtx_unlock_ex(&autofs_softc->sc_lock);
228                         break;
229                 }
230
231                 cv_broadcast(&autofs_softc->sc_cv);
232                 mtx_unlock_ex(&autofs_softc->sc_lock);
233
234                 tsleep(&dummy, 0, "autofs_umount", hz);
235         }
236
237         mtx_lock_ex_quick(&amp->am_lock);
238         while (!RB_EMPTY(&amp->am_root->an_children)) {
239                 struct autofs_node *anp;
240                 /*
241                  * Force delete all nodes when more than one level of
242                  * directories are created via indirect map. Autofs doesn't
243                  * support rmdir(2), thus this is the only way to get out.
244                  */
245                 anp = RB_MIN(autofs_node_tree, &amp->am_root->an_children);
246                 while (!RB_EMPTY(&anp->an_children))
247                         anp = RB_MIN(autofs_node_tree, &anp->an_children);
248                 autofs_node_delete(anp);
249         }
250         autofs_node_delete(amp->am_root);
251         mp->mnt_data = NULL;
252         mtx_unlock_ex(&amp->am_lock);
253
254         mtx_uninit(&amp->am_lock);
255
256         kfree(amp, M_AUTOFS);
257
258         return (0);
259 }
260
261 static int
262 autofs_root(struct mount *mp, struct vnode **vpp)
263 {
264         struct autofs_mount *amp = VFSTOAUTOFS(mp);
265         int error;
266
267         KASSERT(amp->am_root, ("no root node"));
268
269         error = autofs_node_vn(amp->am_root, mp, LK_EXCLUSIVE, vpp);
270         if (error == 0) {
271                 struct vnode *vp = *vpp;
272                 vp->v_flag |= VROOT;
273                 KKASSERT(vp->v_type == VDIR);
274         }
275
276         return (error);
277 }
278
279 static int
280 autofs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
281 {
282         sbp->f_bsize = S_BLKSIZE;
283         sbp->f_iosize = 0;
284         sbp->f_blocks = 0;
285         sbp->f_bfree = 0;
286         sbp->f_bavail = 0;
287         sbp->f_files = 0;
288         sbp->f_ffree = 0;
289
290         return (0);
291 }
292
293 static int
294 autofs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
295 {
296         sbp->f_bsize = S_BLKSIZE;
297         sbp->f_frsize = 0;
298         sbp->f_blocks = 0;
299         sbp->f_bfree = 0;
300         sbp->f_bavail = 0;
301         sbp->f_files = 0;
302         sbp->f_ffree = 0;
303
304         return (0);
305 }
306
307 static struct vfsops autofs_vfsops = {
308         .vfs_mount =            autofs_mount,
309         .vfs_unmount =          autofs_unmount,
310         .vfs_root =             autofs_root,
311         .vfs_statfs =           autofs_statfs,
312         .vfs_statvfs =          autofs_statvfs,
313         .vfs_init =             autofs_init,
314         .vfs_uninit =           autofs_uninit,
315 };
316
317 VFS_SET(autofs_vfsops, autofs, VFCF_SYNTHETIC | VFCF_MPSAFE);
318 MODULE_VERSION(autofs, 1);