1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2001 Dag-Erling Sm��rgrav
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
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 | CTLFLAG_MPSAFE, 0,
53    "pseudofs");
54
55#ifdef PSEUDOFS_TRACE
56int pfs_trace;
57SYSCTL_INT(_vfs_pfs, OID_AUTO, trace, CTLFLAG_RW, &pfs_trace, 0,
58    "enable tracing of pseudofs vnode operations");
59#endif
60
61#if PFS_FSNAMELEN != MFSNAMELEN
62#error "PFS_FSNAMELEN is not equal to MFSNAMELEN"
63#endif
64
65/*
66 * Allocate and initialize a node
67 */
68static struct pfs_node *
69pfs_alloc_node_flags(struct pfs_info *pi, const char *name, pfs_type_t type, int flags)
70{
71	struct pfs_node *pn;
72	int malloc_flags;
73	size_t len;
74
75	len = strlen(name);
76	KASSERT(len < PFS_NAMELEN,
77	    ("%s(): node name is too long", __func__));
78	if (flags & PFS_NOWAIT)
79		malloc_flags = M_NOWAIT | M_ZERO;
80	else
81		malloc_flags = M_WAITOK | M_ZERO;
82	pn = malloc(sizeof(*pn) + len + 1, M_PFSNODES, malloc_flags);
83	if (pn == NULL)
84		return (NULL);
85	mtx_init(&pn->pn_mutex, "pfs_node", NULL, MTX_DEF | MTX_DUPOK);
86	memcpy(pn->pn_name, name, len);
87	pn->pn_type = type;
88	pn->pn_info = pi;
89	return (pn);
90}
91
92static struct pfs_node *
93pfs_alloc_node(struct pfs_info *pi, const char *name, pfs_type_t type)
94{
95	return (pfs_alloc_node_flags(pi, name, type, 0));
96}
97
98/*
99 * Add a node to a directory
100 */
101static void
102pfs_add_node(struct pfs_node *parent, struct pfs_node *pn)
103{
104#ifdef INVARIANTS
105	struct pfs_node *iter;
106#endif
107
108	KASSERT(parent != NULL,
109	    ("%s(): parent is NULL", __func__));
110	KASSERT(pn->pn_parent == NULL,
111	    ("%s(): node already has a parent", __func__));
112	KASSERT(parent->pn_info != NULL,
113	    ("%s(): parent has no pn_info", __func__));
114	KASSERT(parent->pn_type == pfstype_dir ||
115	    parent->pn_type == pfstype_procdir ||
116	    parent->pn_type == pfstype_root,
117	    ("%s(): parent is not a directory", __func__));
118
119#ifdef INVARIANTS
120	/* XXX no locking! */
121	if (pn->pn_type == pfstype_procdir)
122		for (iter = parent; iter != NULL; iter = iter->pn_parent)
123			KASSERT(iter->pn_type != pfstype_procdir,
124			    ("%s(): nested process directories", __func__));
125	for (iter = parent->pn_nodes; iter != NULL; iter = iter->pn_next) {
126		KASSERT(strcmp(pn->pn_name, iter->pn_name) != 0,
127		    ("%s(): homonymous siblings", __func__));
128		if (pn->pn_type == pfstype_procdir)
129			KASSERT(iter->pn_type != pfstype_procdir,
130			    ("%s(): sibling process directories", __func__));
131	}
132#endif
133
134	pn->pn_parent = parent;
135	pfs_fileno_alloc(pn);
136
137	pfs_lock(parent);
138	if ((parent->pn_flags & PFS_PROCDEP) != 0)
139		pn->pn_flags |= PFS_PROCDEP;
140	if (parent->pn_nodes == NULL) {
141		KASSERT(parent->pn_last_node == NULL,
142		    ("%s(): pn_last_node not NULL", __func__));
143		parent->pn_nodes = pn;
144		parent->pn_last_node = pn;
145	} else {
146		KASSERT(parent->pn_last_node != NULL,
147		    ("%s(): pn_last_node is NULL", __func__));
148		KASSERT(parent->pn_last_node->pn_next == NULL,
149		    ("%s(): pn_last_node->pn_next not NULL", __func__));
150		parent->pn_last_node->pn_next = pn;
151		parent->pn_last_node = pn;
152	}
153	pfs_unlock(parent);
154}
155
156/*
157 * Detach a node from its aprent
158 */
159static void
160pfs_detach_node(struct pfs_node *pn)
161{
162	struct pfs_node *node, *parent = pn->pn_parent;
163	struct pfs_node **iter;
164
165	KASSERT(parent != NULL, ("%s(): node has no parent", __func__));
166	KASSERT(parent->pn_info == pn->pn_info,
167	    ("%s(): parent has different pn_info", __func__));
168
169	pfs_lock(parent);
170	if (pn == parent->pn_last_node) {
171		if (pn == pn->pn_nodes) {
172			parent->pn_last_node = NULL;
173		} else {
174			for (node = parent->pn_nodes;
175			    node->pn_next != pn; node = node->pn_next)
176				continue;
177			parent->pn_last_node = node;
178		}
179	}
180	iter = &parent->pn_nodes;
181	while (*iter != NULL) {
182		if (*iter == pn) {
183			*iter = pn->pn_next;
184			break;
185		}
186		iter = &(*iter)->pn_next;
187	}
188	pn->pn_parent = NULL;
189	pfs_unlock(parent);
190}
191
192/*
193 * Add . and .. to a directory
194 */
195static int
196pfs_fixup_dir_flags(struct pfs_node *parent, int flags)
197{
198	struct pfs_node *dot, *dotdot;
199
200	dot = pfs_alloc_node_flags(parent->pn_info, ".", pfstype_this, flags);
201	if (dot == NULL)
202		return (ENOMEM);
203	dotdot = pfs_alloc_node_flags(parent->pn_info, "..", pfstype_parent, flags);
204	if (dotdot == NULL) {
205		pfs_destroy(dot);
206		return (ENOMEM);
207	}
208	pfs_add_node(parent, dot);
209	pfs_add_node(parent, dotdot);
210	return (0);
211}
212
213static void
214pfs_fixup_dir(struct pfs_node *parent)
215{
216
217	pfs_fixup_dir_flags(parent, 0);
218}
219
220/*
221 * Create a directory
222 */
223struct pfs_node	*
224pfs_create_dir(struct pfs_node *parent, const char *name,
225	       pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
226	       int flags)
227{
228	struct pfs_node *pn;
229	int rc;
230
231	pn = pfs_alloc_node_flags(parent->pn_info, name,
232			 (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir, flags);
233	if (pn == NULL)
234		return (NULL);
235	pn->pn_attr = attr;
236	pn->pn_vis = vis;
237	pn->pn_destroy = destroy;
238	pn->pn_flags = flags;
239	pfs_add_node(parent, pn);
240	rc = pfs_fixup_dir_flags(pn, flags);
241	if (rc) {
242		pfs_destroy(pn);
243		return (NULL);
244	}
245	return (pn);
246}
247
248/*
249 * Create a file
250 */
251struct pfs_node	*
252pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill,
253		pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
254		int flags)
255{
256	struct pfs_node *pn;
257
258	pn = pfs_alloc_node_flags(parent->pn_info, name, pfstype_file, flags);
259	if (pn == NULL)
260		return (NULL);
261	pn->pn_fill = fill;
262	pn->pn_attr = attr;
263	pn->pn_vis = vis;
264	pn->pn_destroy = destroy;
265	pn->pn_flags = flags;
266	pfs_add_node(parent, pn);
267
268	return (pn);
269}
270
271/*
272 * Create a symlink
273 */
274struct pfs_node	*
275pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill,
276		pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy,
277		int flags)
278{
279	struct pfs_node *pn;
280
281	pn = pfs_alloc_node_flags(parent->pn_info, name, pfstype_symlink, flags);
282	if (pn == NULL)
283		return (NULL);
284	pn->pn_fill = fill;
285	pn->pn_attr = attr;
286	pn->pn_vis = vis;
287	pn->pn_destroy = destroy;
288	pn->pn_flags = flags;
289	pfs_add_node(parent, pn);
290
291	return (pn);
292}
293
294/*
295 * Locate a node by name
296 */
297struct pfs_node *
298pfs_find_node(struct pfs_node *parent, const char *name)
299{
300	struct pfs_node *pn;
301
302	pfs_lock(parent);
303	for (pn = parent->pn_nodes; pn != NULL; pn = pn->pn_next)
304		if (strcmp(pn->pn_name, name) == 0)
305			break;
306	pfs_unlock(parent);
307	return (pn);
308}
309
310/*
311 * Destroy a node and all its descendants.  If the node to be destroyed
312 * has a parent, the parent's mutex must be held.
313 */
314int
315pfs_destroy(struct pfs_node *pn)
316{
317	struct pfs_node *iter;
318
319	KASSERT(pn != NULL,
320	    ("%s(): node is NULL", __func__));
321	KASSERT(pn->pn_info != NULL,
322	    ("%s(): node has no pn_info", __func__));
323
324	if (pn->pn_parent)
325		pfs_detach_node(pn);
326
327	/* destroy children */
328	if (pn->pn_type == pfstype_dir ||
329	    pn->pn_type == pfstype_procdir ||
330	    pn->pn_type == pfstype_root) {
331		pfs_lock(pn);
332		while (pn->pn_nodes != NULL) {
333			iter = pn->pn_nodes;
334			pn->pn_nodes = iter->pn_next;
335			iter->pn_parent = NULL;
336			pfs_unlock(pn);
337			pfs_destroy(iter);
338			pfs_lock(pn);
339		}
340		pfs_unlock(pn);
341	}
342
343	/* revoke vnodes and fileno */
344	pfs_purge(pn);
345
346	/* callback to free any private resources */
347	if (pn->pn_destroy != NULL)
348		pn_destroy(pn);
349
350	/* destroy the node */
351	pfs_fileno_free(pn);
352	mtx_destroy(&pn->pn_mutex);
353	free(pn, M_PFSNODES);
354
355	return (0);
356}
357
358/*
359 * Mount a pseudofs instance
360 */
361int
362pfs_mount(struct pfs_info *pi, struct mount *mp)
363{
364	struct statfs *sbp;
365
366	if (mp->mnt_flag & MNT_UPDATE)
367		return (EOPNOTSUPP);
368
369	MNT_ILOCK(mp);
370	mp->mnt_flag |= MNT_LOCAL;
371	mp->mnt_kern_flag |= MNTK_NOMSYNC;
372	MNT_IUNLOCK(mp);
373	mp->mnt_data = pi;
374	vfs_getnewfsid(mp);
375
376	sbp = &mp->mnt_stat;
377	vfs_mountedfrom(mp, pi->pi_name);
378	sbp->f_bsize = PAGE_SIZE;
379	sbp->f_iosize = PAGE_SIZE;
380	sbp->f_blocks = 2;
381	sbp->f_bfree = 2;
382	sbp->f_bavail = 2;
383	sbp->f_files = 0;
384	sbp->f_ffree = 0;
385
386	return (0);
387}
388
389/*
390 * Compatibility shim for old mount(2) system call
391 */
392int
393pfs_cmount(struct mntarg *ma, void *data, uint64_t flags)
394{
395	int error;
396
397	error = kernel_mount(ma, flags);
398	return (error);
399}
400
401/*
402 * Unmount a pseudofs instance
403 */
404int
405pfs_unmount(struct mount *mp, int mntflags)
406{
407	int error;
408
409	error = vflush(mp, 0, (mntflags & MNT_FORCE) ?  FORCECLOSE : 0,
410	    curthread);
411	return (error);
412}
413
414/*
415 * Return a root vnode
416 */
417int
418pfs_root(struct mount *mp, int flags, struct vnode **vpp)
419{
420	struct pfs_info *pi;
421
422	pi = (struct pfs_info *)mp->mnt_data;
423	return (pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID));
424}
425
426/*
427 * Return filesystem stats
428 */
429int
430pfs_statfs(struct mount *mp, struct statfs *sbp)
431{
432	/* no-op:  always called with mp->mnt_stat */
433	return (0);
434}
435
436/*
437 * Initialize a pseudofs instance
438 */
439int
440pfs_init(struct pfs_info *pi, struct vfsconf *vfc)
441{
442	struct pfs_node *root;
443	int error;
444
445	pfs_fileno_init(pi);
446
447	/* set up the root directory */
448	root = pfs_alloc_node(pi, "/", pfstype_root);
449	pi->pi_root = root;
450	pfs_fileno_alloc(root);
451	pfs_fixup_dir(root);
452
453	/* construct file hierarchy */
454	error = (pi->pi_init)(pi, vfc);
455	if (error) {
456		pfs_destroy(root);
457		pi->pi_root = NULL;
458		return (error);
459	}
460
461	if (bootverbose)
462		printf("%s registered\n", pi->pi_name);
463	return (0);
464}
465
466/*
467 * Destroy a pseudofs instance
468 */
469int
470pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc)
471{
472	int error;
473
474	pfs_destroy(pi->pi_root);
475	pi->pi_root = NULL;
476	pfs_fileno_uninit(pi);
477	if (bootverbose)
478		printf("%s unregistered\n", pi->pi_name);
479	error = (pi->pi_uninit)(pi, vfc);
480	return (error);
481}
482
483/*
484 * Handle load / unload events
485 */
486static int
487pfs_modevent(module_t mod, int evt, void *arg)
488{
489	switch (evt) {
490	case MOD_LOAD:
491		pfs_vncache_load();
492		break;
493	case MOD_UNLOAD:
494	case MOD_SHUTDOWN:
495		pfs_vncache_unload();
496		break;
497	default:
498		return EOPNOTSUPP;
499		break;
500	}
501	return 0;
502}
503
504/*
505 * Module declaration
506 */
507static moduledata_t pseudofs_data = {
508	"pseudofs",
509	pfs_modevent,
510	NULL
511};
512DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST);
513MODULE_VERSION(pseudofs, 1);
514