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         NULL,
  65         0
  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 }