xref: /dragonfly/sys/vfs/autofs/autofs_vfsops.c (revision 63bc498493e0888c7c5cc0b504fac8c967c31e74)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2016 Tomohiro Kusumi <tkusumi@netbsd.org>
5  * Copyright (c) 2016 The DragonFly Project
6  * Copyright (c) 2014 The FreeBSD Foundation
7  * All rights reserved.
8  *
9  * This software was developed by Edward Tomasz Napierala under sponsorship
10  * from the FreeBSD Foundation.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  */
34 
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/stat.h>
38 
39 #include "autofs.h"
40 #include "autofs_mount.h"
41 
42 static int          autofs_statfs(struct mount *mp, struct statfs *sbp,
43                         struct ucred *cred);
44 
45 static struct objcache_malloc_args autofs_request_args = {
46           sizeof(struct autofs_request), M_AUTOFS,
47 };
48 static struct objcache_malloc_args autofs_node_args = {
49           sizeof(struct autofs_node), M_AUTOFS,
50 };
51 
52 static int
autofs_init(struct vfsconf * vfsp)53 autofs_init(struct vfsconf *vfsp)
54 {
55           KASSERT(autofs_softc == NULL,
56               ("softc %p, should be NULL", autofs_softc));
57 
58           autofs_softc = kmalloc(sizeof(*autofs_softc), M_AUTOFS,
59               M_WAITOK | M_ZERO);
60 
61           autofs_request_objcache = objcache_create("autofs_request", 0, 0,
62                     NULL, NULL, NULL,
63                     objcache_malloc_alloc_zero, objcache_malloc_free,
64                     &autofs_request_args);
65 
66           autofs_node_objcache = objcache_create("autofs_node", 0, 0,
67                     NULL, NULL, NULL,
68                     objcache_malloc_alloc_zero, objcache_malloc_free,
69                     &autofs_node_args);
70 
71           TAILQ_INIT(&autofs_softc->sc_requests);
72           cv_init(&autofs_softc->sc_cv, "autofscv");
73           mtx_init(&autofs_softc->sc_lock, "autofssclk");
74           autofs_softc->sc_dev_opened = false;
75 
76           autofs_softc->sc_cdev = make_dev(&autofs_ops, 0, UID_ROOT, GID_OPERATOR,
77               0640, "autofs");
78           if (autofs_softc->sc_cdev == NULL) {
79                     AUTOFS_WARN("failed to create device node");
80                     objcache_destroy(autofs_request_objcache);
81                     objcache_destroy(autofs_node_objcache);
82                     kfree(autofs_softc, M_AUTOFS);
83                     return (ENODEV);
84           }
85           autofs_softc->sc_cdev->si_drv1 = autofs_softc;
86 
87           return (0);
88 }
89 
90 static int
autofs_uninit(struct vfsconf * vfsp)91 autofs_uninit(struct vfsconf *vfsp)
92 {
93           mtx_lock_ex_quick(&autofs_softc->sc_lock);
94           if (autofs_softc->sc_dev_opened) {
95                     mtx_unlock_ex(&autofs_softc->sc_lock);
96                     return (EBUSY);
97           }
98 
99           if (autofs_softc->sc_cdev != NULL)
100                     destroy_dev(autofs_softc->sc_cdev);
101 
102           objcache_destroy(autofs_request_objcache);
103           objcache_destroy(autofs_node_objcache);
104 
105           mtx_unlock_ex(&autofs_softc->sc_lock);
106 
107           kfree(autofs_softc, M_AUTOFS); /* race with open */
108           autofs_softc = NULL;
109 
110           return (0);
111 }
112 
113 static int
autofs_mount(struct mount * mp,char * mntpt,caddr_t data,struct ucred * cred)114 autofs_mount(struct mount *mp, char *mntpt, caddr_t data, struct ucred *cred)
115 {
116           struct autofs_mount_info info;
117           struct autofs_mount *amp;
118           struct statfs *sbp = &mp->mnt_stat;
119           int error;
120 
121           if (mp->mnt_flag & MNT_UPDATE) {
122                     autofs_flush(VFSTOAUTOFS(mp));
123                     return (0);
124           }
125 
126           error = copyin(data, &info, sizeof(info));
127           if (error)
128                     return (error);
129 
130           /*
131            * Copy-in ->f_mntfromname string.
132            */
133           memset(sbp->f_mntfromname, 0, sizeof(sbp->f_mntfromname));
134           error = copyinstr(info.from, sbp->f_mntfromname,
135               sizeof(sbp->f_mntfromname), NULL);
136           if (error)
137                     return (error);
138           /*
139            * Copy-in ->f_mntonname string.
140            */
141           memset(sbp->f_mntonname, 0, sizeof(sbp->f_mntonname));
142           error = copyinstr(mntpt, sbp->f_mntonname, sizeof(sbp->f_mntonname),
143               NULL);
144           if (error)
145                     return (error);
146 
147           /*
148            * Allocate the autofs mount.
149            */
150           amp = kmalloc(sizeof(*amp), M_AUTOFS, M_WAITOK | M_ZERO);
151           mp->mnt_data = (qaddr_t)amp;
152           strlcpy(amp->am_from, sbp->f_mntfromname, sizeof(amp->am_from));
153           strlcpy(amp->am_on, sbp->f_mntonname, sizeof(amp->am_on));
154 
155           /*
156            * Copy-in master_options string.
157            */
158           error = copyinstr(info.master_options, amp->am_options,
159               sizeof(amp->am_options), NULL);
160           if (error)
161                     goto fail;
162           /*
163            * Copy-in master_prefix string.
164            */
165           error = copyinstr(info.master_prefix, amp->am_prefix,
166               sizeof(amp->am_prefix), NULL);
167           if (error)
168                     goto fail;
169 
170           /*
171            * Initialize the autofs mount.
172            */
173           mtx_init(&amp->am_lock, "autofsmnlk");
174           amp->am_last_ino = AUTOFS_ROOTINO;
175 
176           mtx_lock_ex_quick(&amp->am_lock);
177           error = autofs_node_new(NULL, amp, ".", -1, &amp->am_root);
178           mtx_unlock_ex(&amp->am_lock);
179           KKASSERT(error == 0);
180           KKASSERT(amp->am_root->an_ino == AUTOFS_ROOTINO);
181 
182           vfs_getnewfsid(mp);
183           vfs_add_vnodeops(mp, &autofs_vnode_vops, &mp->mnt_vn_norm_ops);
184 
185           VFS_STATFS(mp, &mp->mnt_stat, cred);
186 
187           return (0);
188 
189 fail:
190           kfree(amp, M_AUTOFS);
191           return (error);
192 }
193 
194 static int
autofs_unmount(struct mount * mp,int mntflags)195 autofs_unmount(struct mount *mp, int mntflags)
196 {
197           struct autofs_mount *amp = VFSTOAUTOFS(mp);
198           int error, flags;
199 
200           flags = 0;
201           if (mntflags & MNT_FORCE)
202                     flags |= FORCECLOSE;
203           error = vflush(mp, 0, flags);
204           if (error) {
205                     AUTOFS_DEBUG("vflush failed with error %d", error);
206                     return (error);
207           }
208 
209           /*
210            * All vnodes are gone, and new one will not appear - so,
211            * no new triggerings.
212            */
213           for (;;) {
214                     struct autofs_request *ar;
215                     int dummy;
216                     bool found;
217 
218                     found = false;
219                     mtx_lock_ex_quick(&autofs_softc->sc_lock);
220                     TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) {
221                               if (ar->ar_mount != amp)
222                                         continue;
223                               ar->ar_error = ENXIO;
224                               ar->ar_done = true;
225                               ar->ar_in_progress = false;
226                               found = true;
227                     }
228                     if (found == false) {
229                               mtx_unlock_ex(&autofs_softc->sc_lock);
230                               break;
231                     }
232 
233                     cv_broadcast(&autofs_softc->sc_cv);
234                     mtx_unlock_ex(&autofs_softc->sc_lock);
235 
236                     tsleep(&dummy, 0, "autofs_umount", hz);
237           }
238 
239           mtx_lock_ex_quick(&amp->am_lock);
240           while (!RB_EMPTY(&amp->am_root->an_children)) {
241                     struct autofs_node *anp;
242                     /*
243                      * Force delete all nodes when more than one level of
244                      * directories are created via indirect map. Autofs doesn't
245                      * support rmdir(2), thus this is the only way to get out.
246                      */
247                     anp = RB_MIN(autofs_node_tree, &amp->am_root->an_children);
248                     while (!RB_EMPTY(&anp->an_children))
249                               anp = RB_MIN(autofs_node_tree, &anp->an_children);
250                     autofs_node_delete(anp);
251           }
252           autofs_node_delete(amp->am_root);
253           mp->mnt_data = NULL;
254           mtx_unlock_ex(&amp->am_lock);
255 
256           mtx_uninit(&amp->am_lock);
257 
258           kfree(amp, M_AUTOFS);
259 
260           return (0);
261 }
262 
263 static int
autofs_root(struct mount * mp,struct vnode ** vpp)264 autofs_root(struct mount *mp, struct vnode **vpp)
265 {
266           struct autofs_mount *amp = VFSTOAUTOFS(mp);
267           int error;
268 
269           KASSERT(amp->am_root, ("no root node"));
270 
271           error = autofs_node_vn(amp->am_root, mp, LK_EXCLUSIVE, vpp);
272           if (error == 0) {
273                     struct vnode *vp = *vpp;
274                     vp->v_flag |= VROOT;
275                     KKASSERT(vp->v_type == VDIR);
276           }
277 
278           return (error);
279 }
280 
281 static int
autofs_statfs(struct mount * mp,struct statfs * sbp,struct ucred * cred)282 autofs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
283 {
284           sbp->f_bsize = S_BLKSIZE;
285           sbp->f_iosize = 0;
286           sbp->f_blocks = 0;
287           sbp->f_bfree = 0;
288           sbp->f_bavail = 0;
289           sbp->f_files = 0;
290           sbp->f_ffree = 0;
291 
292           return (0);
293 }
294 
295 static int
autofs_statvfs(struct mount * mp,struct statvfs * sbp,struct ucred * cred)296 autofs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
297 {
298           sbp->f_bsize = S_BLKSIZE;
299           sbp->f_frsize = 0;
300           sbp->f_blocks = 0;
301           sbp->f_bfree = 0;
302           sbp->f_bavail = 0;
303           sbp->f_files = 0;
304           sbp->f_ffree = 0;
305 
306           return (0);
307 }
308 
309 static struct vfsops autofs_vfsops = {
310           .vfs_flags =                  0,
311           .vfs_mount =                  autofs_mount,
312           .vfs_unmount =                autofs_unmount,
313           .vfs_root =                   autofs_root,
314           .vfs_statfs =                 autofs_statfs,
315           .vfs_statvfs =                autofs_statvfs,
316           .vfs_init =                   autofs_init,
317           .vfs_uninit =                 autofs_uninit,
318 };
319 
320 VFS_SET(autofs_vfsops, autofs, VFCF_SYNTHETIC | VFCF_MPSAFE);
321 MODULE_VERSION(autofs, 1);
322