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 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 /*
  29  * lxprvfsops.c: vfs operations for /lxprocfs.
  30  */
  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/stat.h>
  40 #include <sys/statvfs.h>
  41 #include <sys/sysmacros.h>
  42 #include <sys/systm.h>
  43 #include <sys/var.h>
  44 #include <sys/vfs.h>
  45 #include <sys/vfs_opreg.h>
  46 #include <sys/vnode.h>
  47 #include <sys/mode.h>
  48 #include <sys/signal.h>
  49 #include <sys/user.h>
  50 #include <sys/mount.h>
  51 #include <sys/bitmap.h>
  52 #include <sys/kmem.h>
  53 #include <sys/policy.h>
  54 #include <sys/modctl.h>
  55 #include <sys/sunddi.h>
  56 #include <sys/sunldi.h>
  57 #include <sys/lx_impl.h>
  58 
  59 #include "lx_proc.h"
  60 
  61 /* Module level parameters */
  62 static int      lxprocfstype;
  63 static dev_t    lxprocdev;
  64 static kmutex_t lxpr_mount_lock;
  65 
  66 int nproc_highbit;      /* highbit(v.v_nproc) */
  67 
  68 static int lxpr_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
  69 static int lxpr_unmount(vfs_t *, int, cred_t *);
  70 static int lxpr_root(vfs_t *, vnode_t **);
  71 static int lxpr_statvfs(vfs_t *, statvfs64_t *);
  72 static int lxpr_init(int, char *);
  73 
  74 static vfsdef_t vfw = {
  75         VFSDEF_VERSION,
  76         "lx_proc",
  77         lxpr_init,
  78         VSW_ZMOUNT,
  79         NULL
  80 };
  81 
  82 /*
  83  * Module linkage information for the kernel.
  84  */
  85 extern struct mod_ops mod_fsops;
  86 
  87 static struct modlfs modlfs = {
  88         &mod_fsops, "generic linux procfs", &vfw
  89 };
  90 
  91 static struct modlinkage modlinkage = {
  92         MODREV_1, (void *)&modlfs, NULL
  93 };
  94 
  95 int
  96 _init(void)
  97 {
  98         return (mod_install(&modlinkage));
  99 }
 100 
 101 int
 102 _info(struct modinfo *modinfop)
 103 {
 104         return (mod_info(&modlinkage, modinfop));
 105 }
 106 
 107 int
 108 _fini(void)
 109 {
 110         int retval;
 111 
 112         /*
 113          * attempt to unload the module
 114          */
 115         if ((retval = mod_remove(&modlinkage)) != 0)
 116                 goto done;
 117 
 118         /*
 119          * destroy lxpr_node cache
 120          */
 121         lxpr_fininodecache();
 122 
 123         /*
 124          * clean out the vfsops and vnodeops
 125          */
 126         (void) vfs_freevfsops_by_type(lxprocfstype);
 127         vn_freevnodeops(lxpr_vnodeops);
 128 
 129         mutex_destroy(&lxpr_mount_lock);
 130 done:
 131         return (retval);
 132 }
 133 
 134 static int
 135 lxpr_init(int fstype, char *name)
 136 {
 137         static const fs_operation_def_t lxpr_vfsops_template[] = {
 138                 VFSNAME_MOUNT,          { .vfs_mount = lxpr_mount },
 139                 VFSNAME_UNMOUNT,        { .vfs_unmount = lxpr_unmount },
 140                 VFSNAME_ROOT,           { .vfs_root = lxpr_root },
 141                 VFSNAME_STATVFS,        { .vfs_statvfs = lxpr_statvfs },
 142                 NULL,                   NULL
 143         };
 144         extern const fs_operation_def_t lxpr_vnodeops_template[];
 145         int error;
 146         major_t dev;
 147 
 148         nproc_highbit = highbit(v.v_proc);
 149         lxprocfstype = fstype;
 150         ASSERT(lxprocfstype != 0);
 151 
 152         mutex_init(&lxpr_mount_lock, NULL, MUTEX_DEFAULT, NULL);
 153 
 154         /*
 155          * Associate VFS ops vector with this fstype.
 156          */
 157         error = vfs_setfsops(fstype, lxpr_vfsops_template, NULL);
 158         if (error != 0) {
 159                 cmn_err(CE_WARN, "lxpr_init: bad vfs ops template");
 160                 return (error);
 161         }
 162 
 163         /*
 164          * Set up vnode ops vector too.
 165          */
 166         error = vn_make_ops(name, lxpr_vnodeops_template, &lxpr_vnodeops);
 167         if (error != 0) {
 168                 (void) vfs_freevfsops_by_type(fstype);
 169                 cmn_err(CE_WARN, "lxpr_init: bad vnode ops template");
 170                 return (error);
 171         }
 172 
 173         /*
 174          * Assign a unique "device" number (used by stat(2)).
 175          */
 176         if ((dev = getudev()) == (major_t)-1) {
 177                 cmn_err(CE_WARN, "lxpr_init: can't get unique device number");
 178                 dev = 0;
 179         }
 180 
 181         /*
 182          * Make the pseudo device
 183          */
 184         lxprocdev = makedevice(dev, 0);
 185 
 186         /*
 187          * Initialise cache for lxpr_nodes
 188          */
 189         lxpr_initnodecache();
 190 
 191         return (0);
 192 }
 193 
 194 static int
 195 lxpr_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
 196 {
 197         lxpr_mnt_t *lxpr_mnt;
 198         zone_t *zone = curproc->p_zone;
 199         ldi_ident_t li;
 200         int err;
 201 
 202         /*
 203          * must be root to mount
 204          */
 205         if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
 206                 return (EPERM);
 207 
 208         /*
 209          * mount point must be a directory
 210          */
 211         if (mvp->v_type != VDIR)
 212                 return (ENOTDIR);
 213 
 214         if (zone == global_zone) {
 215                 zone_t *mntzone;
 216 
 217                 mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
 218                 zone_rele(mntzone);
 219                 if (zone != mntzone)
 220                         return (EBUSY);
 221         }
 222 
 223         /*
 224          * Having the resource be anything but "lxproc" doesn't make sense
 225          */
 226         vfs_setresource(vfsp, "lxproc",0);
 227 
 228         lxpr_mnt = kmem_alloc(sizeof (*lxpr_mnt), KM_SLEEP);
 229 
 230         if ((err = ldi_ident_from_mod(&modlinkage, &li)) != 0) {
 231                 kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
 232                 return (err);
 233         }
 234 
 235         lxpr_mnt->lxprm_li = li;
 236 
 237         mutex_enter(&lxpr_mount_lock);
 238 
 239         /*
 240          * Ensure we don't allow overlaying mounts
 241          */
 242         mutex_enter(&mvp->v_lock);
 243         if ((uap->flags & MS_OVERLAY) == 0 &&
 244             (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
 245                 mutex_exit(&mvp->v_lock);
 246                 mutex_exit(&lxpr_mount_lock);
 247                 kmem_free(lxpr_mnt, sizeof ((*lxpr_mnt)));
 248                 return (EBUSY);
 249         }
 250         mutex_exit(&mvp->v_lock);
 251 
 252         /*
 253          * allocate the first vnode
 254          */
 255         zone_hold(lxpr_mnt->lxprm_zone = zone);
 256 
 257         /* Arbitrarily set the parent vnode to the mounted over directory */
 258         lxpr_mnt->lxprm_node = lxpr_getnode(mvp, LXPR_PROCDIR, NULL, 0);
 259 
 260         /* Correctly set the fs for the root node */
 261         lxpr_mnt->lxprm_node->lxpr_vnode->v_vfsp = vfsp;
 262 
 263         vfs_make_fsid(&vfsp->vfs_fsid, lxprocdev, lxprocfstype);
 264         vfsp->vfs_bsize = DEV_BSIZE;
 265         vfsp->vfs_fstype = lxprocfstype;
 266         vfsp->vfs_data = (caddr_t)lxpr_mnt;
 267         vfsp->vfs_dev = lxprocdev;
 268 
 269         mutex_exit(&lxpr_mount_lock);
 270 
 271         return (0);
 272 }
 273 
 274 static int
 275 lxpr_unmount(vfs_t *vfsp, int flag, cred_t *cr)
 276 {
 277         lxpr_mnt_t *lxpr_mnt = (lxpr_mnt_t *)vfsp->vfs_data;
 278         vnode_t *vp;
 279         int count;
 280 
 281         ASSERT(lxpr_mnt != NULL);
 282         vp = LXPTOV(lxpr_mnt->lxprm_node);
 283 
 284         mutex_enter(&lxpr_mount_lock);
 285 
 286         /*
 287          * must be root to unmount
 288          */
 289         if (secpolicy_fs_unmount(cr, vfsp) != 0) {
 290                 mutex_exit(&lxpr_mount_lock);
 291                 return (EPERM);
 292         }
 293 
 294         /*
 295          * forced unmount is not supported by this file system
 296          */
 297         if (flag & MS_FORCE) {
 298                 mutex_exit(&lxpr_mount_lock);
 299                 return (ENOTSUP);
 300         }
 301 
 302         /*
 303          * Ensure that no vnodes are in use on this mount point.
 304          */
 305         mutex_enter(&vp->v_lock);
 306         count = vp->v_count;
 307         mutex_exit(&vp->v_lock);
 308         if (count > 1) {
 309                 mutex_exit(&lxpr_mount_lock);
 310                 return (EBUSY);
 311         }
 312 
 313 
 314         /*
 315          * purge the dnlc cache for vnode entries
 316          * associated with this file system
 317          */
 318         count = dnlc_purge_vfsp(vfsp, 0);
 319 
 320         /*
 321          * free up the lxprnode
 322          */
 323         lxpr_freenode(lxpr_mnt->lxprm_node);
 324         zone_rele(lxpr_mnt->lxprm_zone);
 325         kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
 326 
 327         mutex_exit(&lxpr_mount_lock);
 328 
 329         return (0);
 330 }
 331 
 332 static int
 333 lxpr_root(vfs_t *vfsp, vnode_t **vpp)
 334 {
 335         lxpr_node_t *lxpnp = ((lxpr_mnt_t *)vfsp->vfs_data)->lxprm_node;
 336         vnode_t *vp = LXPTOV(lxpnp);
 337 
 338         VN_HOLD(vp);
 339         *vpp = vp;
 340         return (0);
 341 }
 342 
 343 static int
 344 lxpr_statvfs(vfs_t *vfsp, statvfs64_t *sp)
 345 {
 346         int n;
 347         dev32_t d32;
 348         extern uint_t nproc;
 349 
 350         n = v.v_proc - nproc;
 351 
 352         bzero((caddr_t)sp, sizeof (*sp));
 353         sp->f_bsize  = DEV_BSIZE;
 354         sp->f_frsize = DEV_BSIZE;
 355         sp->f_blocks = (fsblkcnt64_t)0;
 356         sp->f_bfree  = (fsblkcnt64_t)0;
 357         sp->f_bavail = (fsblkcnt64_t)0;
 358         sp->f_files  = (fsfilcnt64_t)v.v_proc + 2;
 359         sp->f_ffree  = (fsfilcnt64_t)n;
 360         sp->f_favail = (fsfilcnt64_t)n;
 361         (void) cmpldev(&d32, vfsp->vfs_dev);
 362         sp->f_fsid   = d32;
 363         /* It is guaranteed that vsw_name will fit in f_basetype */
 364         (void) strcpy(sp->f_basetype, vfssw[lxprocfstype].vsw_name);
 365         sp->f_flag = vf_to_stf(vfsp->vfs_flag);
 366         sp->f_namemax = 64;          /* quite arbitrary */
 367         bzero(sp->f_fstr, sizeof (sp->f_fstr));
 368 
 369         /* We know f_fstr is 32 chars */
 370         (void) strcpy(sp->f_fstr, "/proc");
 371         (void) strcpy(&sp->f_fstr[6], "/proc");
 372 
 373         return (0);
 374 }