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