xref: /freebsd/sys/fs/pseudofs/pseudofs.c (revision f6c0136c7fb87ab8277221a306291e386fe944fb)
1 /*-
2  * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "opt_pseudofs.h"
33 
34 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/systm.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/mount.h>
41 #include <sys/mutex.h>
42 #include <sys/proc.h>
43 #include <sys/sbuf.h>
44 #include <sys/sysctl.h>
45 #include <sys/vnode.h>
46 
47 #include <fs/pseudofs/pseudofs.h>
48 #include <fs/pseudofs/pseudofs_internal.h>
49 
50 static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes");
51 
52 SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW, 0,
53     "pseudofs");
54 
55 #if PFS_FSNAMELEN != MFSNAMELEN
56 #error "PFS_FSNAMELEN is not equal to MFSNAMELEN"
57 #endif
58 
59 /*
60  * Add a node to a directory
61  */
62 static int
63 _pfs_add_node(struct pfs_node *parent, struct pfs_node *node)
64 {
65 	KASSERT(parent != NULL,
66 	    ("%s(): parent is NULL", __func__));
67 	KASSERT(parent->pn_info != NULL,
68 	    ("%s(): parent has no pn_info", __func__));
69 	KASSERT(parent->pn_type == pfstype_dir ||
70 	    parent->pn_type == pfstype_procdir ||
71 	    parent->pn_type == pfstype_root,
72 	    ("%s(): parent is not a directory", __func__));
73 
74 	/* XXX should check for duplicate names etc. */
75 
76 	mtx_lock(&parent->pn_info->pi_mutex);
77 	node->pn_info = parent->pn_info;
78 	node->pn_parent = parent;
79 	node->pn_next = parent->pn_nodes;
80 	parent->pn_nodes = node;
81 	/* Propagate flag to all child nodes (and thus their vnodes) */
82 	if ((parent->pn_flags & PFS_PROCDEP) != 0)
83 		node->pn_flags |= PFS_PROCDEP;
84 	mtx_unlock(&parent->pn_info->pi_mutex);
85 
86 	return (0);
87 }
88 
89 /*
90  * Add . and .. to a directory
91  */
92 static int
93 _pfs_fixup_dir(struct pfs_node *parent)
94 {
95 	struct pfs_node *dir;
96 
97 	MALLOC(dir, struct pfs_node *, sizeof *dir,
98 	    M_PFSNODES, M_WAITOK|M_ZERO);
99 	dir->pn_name[0] = '.';
100 	dir->pn_type = pfstype_this;
101 
102 	if (_pfs_add_node(parent, dir) != 0) {
103 		FREE(dir, M_PFSNODES);
104 		return (-1);
105 	}
106 
107 	MALLOC(dir, struct pfs_node *, sizeof *dir,
108 	    M_PFSNODES, M_WAITOK|M_ZERO);
109 	dir->pn_name[0] = dir->pn_name[1] = '.';
110 	dir->pn_type = pfstype_parent;
111 
112 	if (_pfs_add_node(parent, dir) != 0) {
113 		FREE(dir, M_PFSNODES);
114 		return (-1);
115 	}
116 
117 	return (0);
118 }
119 
120 /*
121  * Create a directory
122  */
123 struct pfs_node	*
124 pfs_create_dir(struct pfs_node *parent, const char *name,
125 	       pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
126 	       int flags)
127 {
128 	struct pfs_node *dir;
129 
130 	KASSERT(strlen(name) < PFS_NAMELEN,
131 	    ("%s(): node name is too long", __func__));
132 
133 	MALLOC(dir, struct pfs_node *, sizeof *dir,
134 	    M_PFSNODES, M_WAITOK|M_ZERO);
135 	strcpy(dir->pn_name, name);
136 	dir->pn_type = (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir;
137 	dir->pn_attr = attr;
138 	dir->pn_vis = vis;
139 	dir->pn_destroy = destroy;
140 	dir->pn_flags = flags;
141 
142 	if (_pfs_add_node(parent, dir) != 0) {
143 		FREE(dir, M_PFSNODES);
144 		return (NULL);
145 	}
146 
147 	if (_pfs_fixup_dir(dir) != 0) {
148 		pfs_destroy(dir);
149 		return (NULL);
150 	}
151 
152 	return (dir);
153 }
154 
155 /*
156  * Create a file
157  */
158 struct pfs_node	*
159 pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill,
160 		pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
161 		int flags)
162 {
163 	struct pfs_node *node;
164 
165 	KASSERT(strlen(name) < PFS_NAMELEN,
166 	    ("%s(): node name is too long", __func__));
167 
168 	MALLOC(node, struct pfs_node *, sizeof *node,
169 	    M_PFSNODES, M_WAITOK|M_ZERO);
170 	strcpy(node->pn_name, name);
171 	node->pn_type = pfstype_file;
172 	node->pn_func = fill;
173 	node->pn_attr = attr;
174 	node->pn_vis = vis;
175 	node->pn_destroy = destroy;
176 	node->pn_flags = flags;
177 
178 	if (_pfs_add_node(parent, node) != 0) {
179 		FREE(node, M_PFSNODES);
180 		return (NULL);
181 	}
182 
183 	return (node);
184 }
185 
186 /*
187  * Create a symlink
188  */
189 struct pfs_node	*
190 pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill,
191 		pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
192 		int flags)
193 {
194 	struct pfs_node *node;
195 
196 	node = pfs_create_file(parent, name, fill, attr, vis, destroy, flags);
197 	if (node == NULL)
198 		return (NULL);
199 	node->pn_type = pfstype_symlink;
200 	return (node);
201 }
202 
203 /*
204  * Locate a node by name
205  */
206 struct pfs_node *
207 pfs_find_node(struct pfs_node *parent, const char *name)
208 {
209 	struct pfs_node *node;
210 
211 	for (node = parent->pn_nodes; node != NULL; node = node->pn_next)
212 		if (strcmp(node->pn_name, name) == 0)
213 			return (node);
214 	return (NULL);
215 }
216 
217 /*
218  * Destroy a node or a tree of nodes
219  */
220 int
221 pfs_destroy(struct pfs_node *node)
222 {
223 	struct pfs_node *parent, *rover;
224 
225 	KASSERT(node != NULL,
226 	    ("%s(): node is NULL", __func__));
227 	KASSERT(node->pn_info != NULL,
228 	    ("%s(): node has no pn_info", __func__));
229 
230 	/* destroy children */
231 	if (node->pn_type == pfstype_dir ||
232 	    node->pn_type == pfstype_procdir ||
233 	    node->pn_type == pfstype_root)
234 		while (node->pn_nodes != NULL)
235 			pfs_destroy(node->pn_nodes);
236 
237 	/* unlink from parent */
238 	if ((parent = node->pn_parent) != NULL) {
239 		KASSERT(parent->pn_info == node->pn_info,
240 		    ("%s(): parent has different pn_info", __func__));
241 		mtx_lock(&node->pn_info->pi_mutex);
242 		if (parent->pn_nodes == node) {
243 			parent->pn_nodes = node->pn_next;
244 		} else {
245 			rover = parent->pn_nodes;
246 			while (rover->pn_next != NULL) {
247 				if (rover->pn_next == node) {
248 					rover->pn_next = node->pn_next;
249 					break;
250 				}
251 				rover = rover->pn_next;
252 			}
253 		}
254 		mtx_unlock(&node->pn_info->pi_mutex);
255 	}
256 
257 	/* callback to free any private resources */
258 	if(node->pn_destroy != NULL)
259 		(node->pn_destroy)(node);
260 
261 	/* revoke vnodes and release memory */
262 	pfs_disable(node);
263 	FREE(node, M_PFSNODES);
264 
265 	return (0);
266 }
267 
268 /*
269  * Mount a pseudofs instance
270  */
271 int
272 pfs_mount(struct pfs_info *pi, struct mount *mp, struct thread *td)
273 {
274 	struct statfs *sbp;
275 
276 	if (mp->mnt_flag & MNT_UPDATE)
277 		return (EOPNOTSUPP);
278 
279 	mp->mnt_flag |= MNT_LOCAL;
280 	mp->mnt_data = (qaddr_t)pi;
281 	vfs_getnewfsid(mp);
282 
283 	sbp = &mp->mnt_stat;
284 	vfs_mountedfrom(mp, pi->pi_name);
285 	sbp->f_bsize = PAGE_SIZE;
286 	sbp->f_iosize = PAGE_SIZE;
287 	sbp->f_blocks = 1;
288 	sbp->f_bfree = 0;
289 	sbp->f_bavail = 0;
290 	sbp->f_files = 1;
291 	sbp->f_ffree = 0;
292 
293 	return (0);
294 }
295 
296 /*
297  * Compatibility shim for old mount(2) system call.
298  */
299 int
300 pfs_cmount(struct mntarg *ma, void *data, int flags, struct thread *td)
301 {
302 	return kernel_mount(ma, flags);
303 }
304 
305 /*
306  * Unmount a pseudofs instance
307  */
308 int
309 pfs_unmount(struct mount *mp, int mntflags, struct thread *td)
310 {
311 	struct pfs_info *pi;
312 	int error;
313 
314 	pi = (struct pfs_info *)mp->mnt_data;
315 
316 	/* XXX do stuff with pi... */
317 
318 	error = vflush(mp, 0, (mntflags & MNT_FORCE) ?  FORCECLOSE : 0, td);
319 	return (error);
320 }
321 
322 /*
323  * Return a root vnode
324  */
325 int
326 pfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
327 {
328 	struct pfs_info *pi;
329 
330 	pi = (struct pfs_info *)mp->mnt_data;
331 	return pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID);
332 }
333 
334 /*
335  * Return filesystem stats
336  */
337 int
338 pfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
339 {
340 	/* no-op:  always called with mp->mnt_stat */
341 	return (0);
342 }
343 
344 /*
345  * Initialize a pseudofs instance
346  */
347 int
348 pfs_init(struct pfs_info *pi, struct vfsconf *vfc)
349 {
350 	struct pfs_node *root;
351 	int error;
352 
353 	mtx_init(&pi->pi_mutex, "pseudofs", NULL, MTX_DEF);
354 
355 	/* set up the root diretory */
356 	MALLOC(root, struct pfs_node *, sizeof *root,
357 	    M_PFSNODES, M_WAITOK|M_ZERO);
358 	root->pn_type = pfstype_root;
359 	root->pn_name[0] = '/';
360 	root->pn_info = pi;
361 	if (_pfs_fixup_dir(root) != 0) {
362 		FREE(root, M_PFSNODES);
363 		return (ENODEV); /* XXX not really the right errno */
364 	}
365 	pi->pi_root = root;
366 
367 	/* construct file hierarchy */
368 	error = (pi->pi_init)(pi, vfc);
369 	if (error) {
370 		pfs_destroy(root);
371 		pi->pi_root = NULL;
372 		mtx_destroy(&pi->pi_mutex);
373 		return (error);
374 	}
375 
376 	pfs_fileno_init(pi);
377 	if (bootverbose)
378 		printf("%s registered\n", pi->pi_name);
379 	return (0);
380 }
381 
382 /*
383  * Destroy a pseudofs instance
384  */
385 int
386 pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc)
387 {
388 	int error;
389 
390 	pfs_fileno_uninit(pi);
391 	pfs_destroy(pi->pi_root);
392 	pi->pi_root = NULL;
393 	mtx_destroy(&pi->pi_mutex);
394 	if (bootverbose)
395 		printf("%s unregistered\n", pi->pi_name);
396 	error = (pi->pi_uninit)(pi, vfc);
397 	return (error);
398 }
399 
400 /*
401  * Handle load / unload events
402  */
403 static int
404 pfs_modevent(module_t mod, int evt, void *arg)
405 {
406 	switch (evt) {
407 	case MOD_LOAD:
408 		pfs_vncache_load();
409 		break;
410 	case MOD_UNLOAD:
411 	case MOD_SHUTDOWN:
412 		pfs_vncache_unload();
413 		break;
414 	default:
415 		return EOPNOTSUPP;
416 		break;
417 	}
418 	return 0;
419 }
420 
421 /*
422  * Module declaration
423  */
424 static moduledata_t pseudofs_data = {
425 	"pseudofs",
426 	pfs_modevent,
427 	NULL
428 };
429 DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST);
430 MODULE_VERSION(pseudofs, 1);
431