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