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 61 static int prinit(); 62 63 static mntopts_t proc_mntopts = { 64 .mo_count = 0, 65 .mo_list = NULL 66 }; 67 68 static 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 */ 79 extern struct mod_ops mod_fsops; 80 81 static struct modlfs modlfs = { 82 &mod_fsops, "filesystem for proc", &vfw 83 }; 84 85 static struct modlinkage modlinkage = { 86 MODREV_1, (void *)&modlfs, NULL 87 }; 88 89 int 90 _init(void) 91 { 92 return (mod_install(&modlinkage)); 93 } 94 95 int 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 108 int nproc_highbit; /* highbit(v.v_nproc) */ 109 110 static int procfstype; 111 static major_t procfs_major; 112 static minor_t procfs_minor; 113 static kmutex_t procfs_minor_lock; 114 115 static kmutex_t pr_mount_lock; 116 117 /* 118 * /proc VFS operations vector. 119 */ 120 static int prmount(), prunmount(), prroot(), prstatvfs(); 121 122 static void 123 prinitrootnode(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 140 static int 141 prinit(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 */ 190 static int 191 prmount(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 */ 249 static int 250 prunmount(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 */ 289 static int 290 prroot(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 299 static int 300 prstatvfs(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