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
50static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes");
51
52SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW, 0,
53    "pseudofs");
54
55int pfs_trace;
56SYSCTL_INT(_vfs_pfs, OID_AUTO, trace, CTLFLAG_RW, &pfs_trace, 0,
57    "enable tracing of pseudofs vnode operations");
58
59#if PFS_FSNAMELEN != MFSNAMELEN
60#error "PFS_FSNAMELEN is not equal to MFSNAMELEN"
61#endif
62
63/*
64 * Allocate and initialize a node
65 */
66static struct pfs_node *
67pfs_alloc_node(struct pfs_info *pi, const char *name, pfs_type_t type)
68{
69	struct pfs_node *pn;
70
71	KASSERT(strlen(name) < PFS_NAMELEN,
72	    ("%s(): node name is too long", __func__));
73
74	pn = malloc(sizeof *pn,
75	    M_PFSNODES, M_WAITOK|M_ZERO);
76	mtx_init(&pn->pn_mutex, "pfs_node", NULL, MTX_DEF | MTX_DUPOK);
77	strlcpy(pn->pn_name, name, sizeof pn->pn_name);
78	pn->pn_type = type;
79	pn->pn_info = pi;
80	return (pn);
81}
82
83/*
84 * Add a node to a directory
85 */
86static void
87pfs_add_node(struct pfs_node *parent, struct pfs_node *pn)
88{
89#ifdef INVARIANTS
90	struct pfs_node *iter;
91#endif
92
93	KASSERT(parent != NULL,
94	    ("%s(): parent is NULL", __func__));
95	KASSERT(pn->pn_parent == NULL,
96	    ("%s(): node already has a parent", __func__));
97	KASSERT(parent->pn_info != NULL,
98	    ("%s(): parent has no pn_info", __func__));
99	KASSERT(parent->pn_type == pfstype_dir ||
100	    parent->pn_type == pfstype_procdir ||
101	    parent->pn_type == pfstype_root,
102	    ("%s(): parent is not a directory", __func__));
103
104#ifdef INVARIANTS
105	/* XXX no locking! */
106	if (pn->pn_type == pfstype_procdir)
107		for (iter = parent; iter != NULL; iter = iter->pn_parent)
108			KASSERT(iter->pn_type != pfstype_procdir,
109			    ("%s(): nested process directories", __func__));
110	for (iter = parent->pn_nodes; iter != NULL; iter = iter->pn_next) {
111		KASSERT(strcmp(pn->pn_name, iter->pn_name) != 0,
112		    ("%s(): homonymous siblings", __func__));
113		if (pn->pn_type == pfstype_procdir)
114			KASSERT(iter->pn_type != pfstype_procdir,
115			    ("%s(): sibling process directories", __func__));
116	}
117#endif
118
119	pn->pn_parent = parent;
120	pfs_fileno_alloc(pn);
121
122	pfs_lock(parent);
123	pn->pn_next = parent->pn_nodes;
124	if ((parent->pn_flags & PFS_PROCDEP) != 0)
125		pn->pn_flags |= PFS_PROCDEP;
126	parent->pn_nodes = pn;
127	pfs_unlock(parent);
128}
129
130/*
131 * Detach a node from its aprent
132 */
133static void
134pfs_detach_node(struct pfs_node *pn)
135{
136	struct pfs_node *parent = pn->pn_parent;
137	struct pfs_node **iter;
138
139	KASSERT(parent != NULL, ("%s(): node has no parent", __func__));
140	KASSERT(parent->pn_info == pn->pn_info,
141	    ("%s(): parent has different pn_info", __func__));
142
143	pfs_lock(parent);
144	iter = &parent->pn_nodes;
145	while (*iter != NULL) {
146		if (*iter == pn) {
147			*iter = pn->pn_next;
148			break;
149		}
150		iter = &(*iter)->pn_next;
151	}
152	pn->pn_parent = NULL;
153	pfs_unlock(parent);
154}
155
156/*
157 * Add . and .. to a directory
158 */
159static void
160pfs_fixup_dir(struct pfs_node *parent)
161{
162	struct pfs_node *pn;
163
164	pn = pfs_alloc_node(parent->pn_info, ".", pfstype_this);
165	pfs_add_node(parent, pn);
166	pn = pfs_alloc_node(parent->pn_info, "..", pfstype_parent);
167	pfs_add_node(parent, pn);
168}
169
170/*
171 * Create a directory
172 */
173struct pfs_node	*
174pfs_create_dir(struct pfs_node *parent, const char *name,
175	       pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
176	       int flags)
177{
178	struct pfs_node *pn;
179
180	pn = pfs_alloc_node(parent->pn_info, name,
181	    (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir);
182	pn->pn_attr = attr;
183	pn->pn_vis = vis;
184	pn->pn_destroy = destroy;
185	pn->pn_flags = flags;
186	pfs_add_node(parent, pn);
187	pfs_fixup_dir(pn);
188
189	return (pn);
190}
191
192/*
193 * Create a file
194 */
195struct pfs_node	*
196pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill,
197		pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
198		int flags)
199{
200	struct pfs_node *pn;
201
202	pn = pfs_alloc_node(parent->pn_info, name, pfstype_file);
203	pn->pn_fill = fill;
204	pn->pn_attr = attr;
205	pn->pn_vis = vis;
206	pn->pn_destroy = destroy;
207	pn->pn_flags = flags;
208	pfs_add_node(parent, pn);
209
210	return (pn);
211}
212
213/*
214 * Create a symlink
215 */
216struct pfs_node	*
217pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill,
218		pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
219		int flags)
220{
221	struct pfs_node *pn;
222
223	pn = pfs_alloc_node(parent->pn_info, name, pfstype_symlink);
224	pn->pn_fill = fill;
225	pn->pn_attr = attr;
226	pn->pn_vis = vis;
227	pn->pn_destroy = destroy;
228	pn->pn_flags = flags;
229	pfs_add_node(parent, pn);
230
231	return (pn);
232}
233
234/*
235 * Locate a node by name
236 */
237struct pfs_node *
238pfs_find_node(struct pfs_node *parent, const char *name)
239{
240	struct pfs_node *pn;
241
242	pfs_lock(parent);
243	for (pn = parent->pn_nodes; pn != NULL; pn = pn->pn_next)
244		if (strcmp(pn->pn_name, name) == 0)
245			break;
246	pfs_unlock(parent);
247	return (pn);
248}
249
250/*
251 * Destroy a node and all its descendants.  If the node to be destroyed
252 * has a parent, the parent's mutex must be held.
253 */
254int
255pfs_destroy(struct pfs_node *pn)
256{
257	struct pfs_node *iter;
258
259	KASSERT(pn != NULL,
260	    ("%s(): node is NULL", __func__));
261	KASSERT(pn->pn_info != NULL,
262	    ("%s(): node has no pn_info", __func__));
263
264	if (pn->pn_parent)
265		pfs_detach_node(pn);
266
267	/* destroy children */
268	if (pn->pn_type == pfstype_dir ||
269	    pn->pn_type == pfstype_procdir ||
270	    pn->pn_type == pfstype_root) {
271		pfs_lock(pn);
272		while (pn->pn_nodes != NULL) {
273			iter = pn->pn_nodes;
274			pn->pn_nodes = iter->pn_next;
275			iter->pn_parent = NULL;
276			pfs_unlock(pn);
277			pfs_destroy(iter);
278			pfs_lock(pn);
279		}
280		pfs_unlock(pn);
281	}
282
283	/* revoke vnodes and fileno */
284	pfs_purge(pn);
285
286	/* callback to free any private resources */
287	if (pn->pn_destroy != NULL)
288		pn_destroy(pn);
289
290	/* destroy the node */
291	pfs_fileno_free(pn);
292	mtx_destroy(&pn->pn_mutex);
293	free(pn, M_PFSNODES);
294
295	return (0);
296}
297
298/*
299 * Mount a pseudofs instance
300 */
301int
302pfs_mount(struct pfs_info *pi, struct mount *mp)
303{
304	struct statfs *sbp;
305
306	if (mp->mnt_flag & MNT_UPDATE)
307		return (EOPNOTSUPP);
308
309	MNT_ILOCK(mp);
310	mp->mnt_flag |= MNT_LOCAL;
311	mp->mnt_kern_flag |= MNTK_MPSAFE;
312	MNT_IUNLOCK(mp);
313	mp->mnt_data = pi;
314	vfs_getnewfsid(mp);
315
316	sbp = &mp->mnt_stat;
317	vfs_mountedfrom(mp, pi->pi_name);
318	sbp->f_bsize = PAGE_SIZE;
319	sbp->f_iosize = PAGE_SIZE;
320	sbp->f_blocks = 1;
321	sbp->f_bfree = 0;
322	sbp->f_bavail = 0;
323	sbp->f_files = 1;
324	sbp->f_ffree = 0;
325
326	return (0);
327}
328
329/*
330 * Compatibility shim for old mount(2) system call
331 */
332int
333pfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
334{
335	int error;
336
337	error = kernel_mount(ma, flags);
338	return (error);
339}
340
341/*
342 * Unmount a pseudofs instance
343 */
344int
345pfs_unmount(struct mount *mp, int mntflags)
346{
347	int error;
348
349	error = vflush(mp, 0, (mntflags & MNT_FORCE) ?  FORCECLOSE : 0,
350	    curthread);
351	return (error);
352}
353
354/*
355 * Return a root vnode
356 */
357int
358pfs_root(struct mount *mp, int flags, struct vnode **vpp)
359{
360	struct pfs_info *pi;
361
362	pi = (struct pfs_info *)mp->mnt_data;
363	return (pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID));
364}
365
366/*
367 * Return filesystem stats
368 */
369int
370pfs_statfs(struct mount *mp, struct statfs *sbp)
371{
372	/* no-op:  always called with mp->mnt_stat */
373	return (0);
374}
375
376/*
377 * Initialize a pseudofs instance
378 */
379int
380pfs_init(struct pfs_info *pi, struct vfsconf *vfc)
381{
382	struct pfs_node *root;
383	int error;
384
385	mtx_assert(&Giant, MA_OWNED);
386
387	pfs_fileno_init(pi);
388
389	/* set up the root diretory */
390	root = pfs_alloc_node(pi, "/", pfstype_root);
391	pi->pi_root = root;
392	pfs_fileno_alloc(root);
393	pfs_fixup_dir(root);
394
395	/* construct file hierarchy */
396	error = (pi->pi_init)(pi, vfc);
397	if (error) {
398		pfs_destroy(root);
399		pi->pi_root = NULL;
400		return (error);
401	}
402
403	if (bootverbose)
404		printf("%s registered\n", pi->pi_name);
405	return (0);
406}
407
408/*
409 * Destroy a pseudofs instance
410 */
411int
412pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc)
413{
414	int error;
415
416	mtx_assert(&Giant, MA_OWNED);
417
418	pfs_destroy(pi->pi_root);
419	pi->pi_root = NULL;
420	pfs_fileno_uninit(pi);
421	if (bootverbose)
422		printf("%s unregistered\n", pi->pi_name);
423	error = (pi->pi_uninit)(pi, vfc);
424	return (error);
425}
426
427/*
428 * Handle load / unload events
429 */
430static int
431pfs_modevent(module_t mod, int evt, void *arg)
432{
433	switch (evt) {
434	case MOD_LOAD:
435		pfs_vncache_load();
436		break;
437	case MOD_UNLOAD:
438	case MOD_SHUTDOWN:
439		pfs_vncache_unload();
440		break;
441	default:
442		return EOPNOTSUPP;
443		break;
444	}
445	return 0;
446}
447
448/*
449 * Module declaration
450 */
451static moduledata_t pseudofs_data = {
452	"pseudofs",
453	pfs_modevent,
454	NULL
455};
456DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST);
457MODULE_VERSION(pseudofs, 1);
458