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