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