1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
26/*	  All Rights Reserved	*/
27
28
29#include <sys/types.h>
30#include <sys/param.h>
31#include <sys/cmn_err.h>
32#include <sys/cred.h>
33#include <sys/debug.h>
34#include <sys/errno.h>
35#include <sys/proc.h>
36#include <sys/procfs.h>
37#include <sys/stat.h>
38#include <sys/statvfs.h>
39#include <sys/sysmacros.h>
40#include <sys/systm.h>
41#include <sys/zone.h>
42#include <sys/var.h>
43#include <sys/vfs.h>
44#include <sys/vfs_opreg.h>
45#include <sys/vnode.h>
46#include <sys/mode.h>
47#include <sys/signal.h>
48#include <sys/user.h>
49#include <sys/mount.h>
50#include <sys/bitmap.h>
51#include <sys/kmem.h>
52#include <sys/policy.h>
53#include <fs/fs_subr.h>
54#include <fs/proc/prdata.h>
55
56/*
57 * This is the loadable module wrapper.
58 */
59#include <sys/modctl.h>
60
61static int prinit();
62
63static mntopts_t proc_mntopts = {
64	.mo_count = 0,
65	.mo_list = NULL
66};
67
68static vfsdef_t vfw = {
69	VFSDEF_VERSION,
70	"proc",
71	prinit,
72	VSW_HASPROTO|VSW_STATS|VSW_XID|VSW_ZMOUNT,
73	&proc_mntopts
74};
75
76/*
77 * Module linkage information for the kernel.
78 */
79extern struct mod_ops mod_fsops;
80
81static struct modlfs modlfs = {
82	&mod_fsops, "filesystem for proc", &vfw
83};
84
85static struct modlinkage modlinkage = {
86	MODREV_1, (void *)&modlfs, NULL
87};
88
89int
90_init(void)
91{
92	return (mod_install(&modlinkage));
93}
94
95int
96_info(struct modinfo *modinfop)
97{
98	return (mod_info(&modlinkage, modinfop));
99}
100
101/*
102 * N.B.
103 * No _fini routine. The module cannot be unloaded once loaded.
104 * The NO_UNLOAD_STUB in modstubs.s must change if this module
105 * is ever modified to become unloadable.
106 */
107
108int		nproc_highbit;		/* highbit(v.v_nproc) */
109
110static int	procfstype;
111static major_t	procfs_major;
112static minor_t	procfs_minor;
113static kmutex_t	procfs_minor_lock;
114
115static kmutex_t	pr_mount_lock;
116
117/*
118 * /proc VFS operations vector.
119 */
120static int	prmount(), prunmount(), prroot(), prstatvfs();
121
122static void
123prinitrootnode(prnode_t *pnp, vfs_t *vfsp)
124{
125	struct vnode *vp;
126
127	bzero((caddr_t)pnp, sizeof (*pnp));
128	pnp->pr_vnode = vp = vn_alloc(KM_SLEEP);
129
130	mutex_init(&pnp->pr_mutex, NULL, MUTEX_DEFAULT, NULL);
131	vp->v_flag = VROOT|VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
132	VN_SET_VFS_TYPE_DEV(vp, vfsp, VDIR, 0);
133	vn_setops(vp, prvnodeops);
134	vp->v_data = (caddr_t)pnp;
135	pnp->pr_type = PR_PROCDIR;
136	pnp->pr_mode = 0555;	/* read-search by everyone */
137	vn_exists(vp);
138}
139
140static int
141prinit(int fstype, char *name)
142{
143	static const fs_operation_def_t pr_vfsops_template[] = {
144		VFSNAME_MOUNT,		{ .vfs_mount = prmount },
145		VFSNAME_UNMOUNT,	{ .vfs_unmount = prunmount },
146		VFSNAME_ROOT,		{ .vfs_root = prroot },
147		VFSNAME_STATVFS,	{ .vfs_statvfs = prstatvfs },
148		NULL,			NULL
149	};
150	extern const fs_operation_def_t pr_vnodeops_template[];
151	int error;
152
153	nproc_highbit = highbit(v.v_proc);
154	procfstype = fstype;
155	ASSERT(procfstype != 0);
156	/*
157	 * Associate VFS ops vector with this fstype.
158	 */
159	error = vfs_setfsops(fstype, pr_vfsops_template, NULL);
160	if (error != 0) {
161		cmn_err(CE_WARN, "prinit: bad vfs ops template");
162		return (error);
163	}
164
165	/*
166	 * Set up vnode ops vector too.
167	 */
168
169	error = vn_make_ops(name, pr_vnodeops_template, &prvnodeops);
170	if (error != 0) {
171		(void) vfs_freevfsops_by_type(fstype);
172		cmn_err(CE_WARN, "prinit: bad vnode ops template");
173		return (error);
174	}
175
176	/*
177	 * Assign a unique "device" number (used by stat(2)).
178	 */
179	if ((procfs_major = getudev()) == (major_t)-1) {
180		cmn_err(CE_WARN, "prinit: can't get unique device number");
181		procfs_major = 0;
182	}
183	mutex_init(&pr_mount_lock, NULL, MUTEX_DEFAULT, NULL);
184	mutex_init(&procfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
185
186	return (0);
187}
188
189/* ARGSUSED */
190static int
191prmount(struct vfs *vfsp, struct vnode *mvp,
192    struct mounta *uap, struct cred *cr)
193{
194	prnode_t *pnp;
195	zone_t *zone = curproc->p_zone;
196
197	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
198		return (EPERM);
199
200	if (mvp->v_type != VDIR)
201		return (ENOTDIR);
202
203	if (zone == global_zone) {
204		zone_t *mntzone;
205
206		mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
207		zone_rele(mntzone);
208		if (zone != mntzone)
209			return (EBUSY);
210	}
211	/*
212	 * Having the resource be anything but "proc" doesn't make sense
213	 */
214	vfs_setresource(vfsp, "proc", 0);
215
216	pnp = kmem_alloc(sizeof (*pnp), KM_SLEEP);
217	mutex_enter(&pr_mount_lock);
218
219	mutex_enter(&mvp->v_lock);
220	if ((uap->flags & MS_OVERLAY) == 0 &&
221	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
222		mutex_exit(&mvp->v_lock);
223		mutex_exit(&pr_mount_lock);
224		kmem_free(pnp, sizeof (*pnp));
225		return (EBUSY);
226	}
227	mutex_exit(&mvp->v_lock);
228
229	prinitrootnode(pnp, vfsp);
230	vfsp->vfs_fstype = procfstype;
231	vfsp->vfs_data = (caddr_t)pnp;
232	vfsp->vfs_bsize = DEV_BSIZE;
233	/*
234	 * find an available minor device number for this mount
235	 */
236	mutex_enter(&procfs_minor_lock);
237	do {
238		vfsp->vfs_dev = makedevice(procfs_major, procfs_minor);
239		procfs_minor = (procfs_minor + 1) & L_MAXMIN32;
240	} while (vfs_devismounted(vfsp->vfs_dev));
241	mutex_exit(&procfs_minor_lock);
242	vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, procfstype);
243
244	mutex_exit(&pr_mount_lock);
245	return (0);
246}
247
248/* ARGSUSED */
249static int
250prunmount(struct vfs *vfsp, int flag, struct cred *cr)
251{
252	prnode_t *pnp = (prnode_t *)vfsp->vfs_data;
253	vnode_t *vp = PTOV(pnp);
254
255	mutex_enter(&pr_mount_lock);
256	if (secpolicy_fs_unmount(cr, vfsp) != 0) {
257		mutex_exit(&pr_mount_lock);
258		return (EPERM);
259	}
260
261	/*
262	 * forced unmount is not supported by this file system
263	 * and thus, ENOTSUP, is being returned.
264	 */
265	if (flag & MS_FORCE) {
266		mutex_exit(&pr_mount_lock);
267		return (ENOTSUP);
268	}
269
270	/*
271	 * Ensure that no /proc vnodes are in use on this mount point.
272	 */
273	mutex_enter(&vp->v_lock);
274	if (vp->v_count > 1) {
275		mutex_exit(&vp->v_lock);
276		mutex_exit(&pr_mount_lock);
277		return (EBUSY);
278	}
279
280	mutex_exit(&vp->v_lock);
281	mutex_exit(&pr_mount_lock);
282	vn_invalid(vp);
283	vn_free(vp);
284	kmem_free(pnp, sizeof (*pnp));
285	return (0);
286}
287
288/* ARGSUSED */
289static int
290prroot(struct vfs *vfsp, struct vnode **vpp)
291{
292	vnode_t *vp = PTOV((prnode_t *)vfsp->vfs_data);
293
294	VN_HOLD(vp);
295	*vpp = vp;
296	return (0);
297}
298
299static int
300prstatvfs(struct vfs *vfsp, struct statvfs64 *sp)
301{
302	int n;
303	dev32_t d32;
304	extern uint_t nproc;
305
306	n = v.v_proc - nproc;
307
308	bzero((caddr_t)sp, sizeof (*sp));
309	sp->f_bsize	= DEV_BSIZE;
310	sp->f_frsize	= DEV_BSIZE;
311	sp->f_blocks	= (fsblkcnt64_t)0;
312	sp->f_bfree	= (fsblkcnt64_t)0;
313	sp->f_bavail	= (fsblkcnt64_t)0;
314	sp->f_files	= (fsfilcnt64_t)v.v_proc + 2;
315	sp->f_ffree	= (fsfilcnt64_t)n;
316	sp->f_favail	= (fsfilcnt64_t)n;
317	(void) cmpldev(&d32, vfsp->vfs_dev);
318	sp->f_fsid	= d32;
319	(void) strcpy(sp->f_basetype, vfssw[procfstype].vsw_name);
320	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
321	sp->f_namemax = 64;		/* quite arbitrary */
322	bzero(sp->f_fstr, sizeof (sp->f_fstr));
323	(void) strcpy(sp->f_fstr, "/proc");
324	(void) strcpy(&sp->f_fstr[6], "/proc");
325	return (0);
326}
327