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) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <sys/types.h>
  26 #include <sys/param.h>
  27 #include <sys/cmn_err.h>
  28 #include <sys/cred.h>
  29 #include <sys/debug.h>
  30 #include <sys/errno.h>
  31 #include <sys/proc.h>
  32 #include <sys/procfs.h>
  33 #include <sys/stat.h>
  34 #include <sys/statvfs.h>
  35 #include <sys/sysmacros.h>
  36 #include <sys/systm.h>
  37 #include <sys/var.h>
  38 #include <sys/vfs.h>
  39 #include <sys/vfs_opreg.h>
  40 #include <sys/vnode.h>
  41 #include <sys/mode.h>
  42 #include <sys/signal.h>
  43 #include <sys/user.h>
  44 #include <sys/mount.h>
  45 #include <sys/bitmap.h>
  46 #include <sys/kmem.h>
  47 #include <sys/policy.h>
  48 #include <fs/fs_subr.h>
  49 #include <sys/fs/mntdata.h>
  50 #include <sys/zone.h>
  51 
  52 /*
  53  * This is the loadable module wrapper.
  54  */
  55 #include <sys/modctl.h>
  56 
  57 static int mntinit(int, char *);
  58 
  59 static mntopts_t mnt_mntopts = {
  60         0,
  61         NULL
  62 };
  63 
  64 static vfsdef_t vfw = {
  65         VFSDEF_VERSION,
  66         "mntfs",
  67         mntinit,
  68         VSW_HASPROTO|VSW_STATS|VSW_ZMOUNT,
  69         &mnt_mntopts
  70 };
  71 
  72 /*
  73  * Module linkage information for the kernel.
  74  */
  75 extern struct mod_ops mod_fsops;
  76 
  77 static struct modlfs modlfs = {
  78         &mod_fsops, "mount information file system", &vfw
  79 };
  80 
  81 static struct modlinkage modlinkage = {
  82         MODREV_1, (void *)&modlfs, NULL
  83 };
  84 
  85 int
  86 _init(void)
  87 {
  88         return (mod_install(&modlinkage));
  89 }
  90 
  91 int
  92 _info(struct modinfo *modinfop)
  93 {
  94         return (mod_info(&modlinkage, modinfop));
  95 }
  96 
  97 /*
  98  * N.B.
  99  * No _fini routine. The module cannot be unloaded once loaded.
 100  * The NO_UNLOAD_STUB in modstubs.s must change if this module
 101  * is ever modified to become unloadable.
 102  */
 103 
 104 extern int      mntfstype;
 105 static major_t  mnt_major;
 106 static minor_t  mnt_minor;
 107 static kmutex_t mnt_minor_lock;
 108 
 109 /*
 110  * /mnttab VFS operations vector.
 111  */
 112 static int      mntmount(), mntunmount(), mntroot(), mntstatvfs();
 113 
 114 static void
 115 mntinitrootnode(mntnode_t *mnp)
 116 {
 117         struct vnode *vp;
 118 
 119         bzero((caddr_t)mnp, sizeof (*mnp));
 120 
 121         mnp->mnt_vnode = vn_alloc(KM_SLEEP);
 122 
 123         vp = MTOV(mnp);
 124 
 125         vp->v_flag = VROOT|VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
 126         vn_setops(vp, mntvnodeops);
 127         vp->v_type = VREG;
 128         vp->v_data = (caddr_t)mnp;
 129 }
 130 
 131 static int
 132 mntinit(int fstype, char *name)
 133 {
 134         static const fs_operation_def_t mnt_vfsops_template[] = {
 135                 VFSNAME_MOUNT,          { .vfs_mount = mntmount },
 136                 VFSNAME_UNMOUNT,        { .vfs_unmount = mntunmount },
 137                 VFSNAME_ROOT,           { .vfs_root = mntroot },
 138                 VFSNAME_STATVFS,        { .vfs_statvfs = mntstatvfs },
 139                 NULL,                   NULL
 140         };
 141         extern const fs_operation_def_t mnt_vnodeops_template[];
 142         int error;
 143 
 144         mntfstype = fstype;
 145         ASSERT(mntfstype != 0);
 146         /*
 147          * Associate VFS ops vector with this fstype.
 148          */
 149         error = vfs_setfsops(fstype, mnt_vfsops_template, NULL);
 150         if (error != 0) {
 151                 cmn_err(CE_WARN, "mntinit: bad vfs ops template");
 152                 return (error);
 153         }
 154 
 155         /* Vnode ops too. */
 156 
 157         error = vn_make_ops(name, mnt_vnodeops_template, &mntvnodeops);
 158         if (error != 0) {
 159                 (void) vfs_freevfsops_by_type(fstype);
 160                 cmn_err(CE_WARN, "mntinit: bad vnode ops template");
 161                 return (error);
 162         }
 163 
 164         /*
 165          * Assign a unique "device" number (used by stat(2)).
 166          */
 167         if ((mnt_major = getudev()) == (major_t)-1) {
 168                 cmn_err(CE_WARN, "mntinit: can't get unique device number");
 169                 mnt_major = 0;
 170         }
 171         mutex_init(&mnt_minor_lock, NULL, MUTEX_DEFAULT, NULL);
 172 
 173         return (0);
 174 }
 175 
 176 /* ARGSUSED */
 177 static int
 178 mntmount(struct vfs *vfsp, struct vnode *mvp,
 179         struct mounta *uap, struct cred *cr)
 180 {
 181         mntdata_t *mnt;
 182         mntnode_t *mnp;
 183         zone_t *zone = curproc->p_zone;
 184 
 185         if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
 186                 return (EPERM);
 187 
 188         /*
 189          * You can only mount mnttab in your current zone.
 190          */
 191         if (zone == global_zone) {
 192                 zone_t *mntzone;
 193 
 194                 mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
 195                 ASSERT(mntzone != NULL);
 196                 zone_rele(mntzone);
 197                 if (mntzone != zone)
 198                         return (EBUSY);
 199         }
 200 
 201         /*
 202          * Having the resource be anything but "mnttab" doesn't make sense
 203          */
 204         vfs_setresource(vfsp, "mnttab", 0);
 205 
 206         mnt = kmem_zalloc(sizeof (*mnt), KM_SLEEP);
 207         mutex_enter(&mvp->v_lock);
 208         if ((uap->flags & MS_OVERLAY) == 0 &&
 209             (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
 210                 mutex_exit(&mvp->v_lock);
 211                 kmem_free(mnt, sizeof (*mnt));
 212                 return (EBUSY);
 213         }
 214         mutex_exit(&mvp->v_lock);
 215 
 216         zone_init_ref(&mnt->mnt_zone_ref);
 217         zone_hold_ref(zone, &mnt->mnt_zone_ref, ZONE_REF_MNTFS);
 218         mnp = &mnt->mnt_node;
 219 
 220         vfsp->vfs_fstype = mntfstype;
 221         vfsp->vfs_data = (caddr_t)mnt;
 222         /*
 223          * find an available minor device number for this mount.
 224          */
 225         mutex_enter(&mnt_minor_lock);
 226         do {
 227                 mnt_minor = (mnt_minor + 1) & L_MAXMIN32;
 228                 vfsp->vfs_dev = makedevice(mnt_major, mnt_minor);
 229         } while (vfs_devismounted(vfsp->vfs_dev));
 230         mutex_exit(&mnt_minor_lock);
 231         vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, mntfstype);
 232         vfsp->vfs_bsize = DEV_BSIZE;
 233         mntinitrootnode(mnp);
 234         MTOV(mnp)->v_vfsp = vfsp;
 235         mnp->mnt_mountvp = mvp;
 236         vn_exists(MTOV(mnp));
 237         return (0);
 238 }
 239 
 240 /* ARGSUSED */
 241 static int
 242 mntunmount(struct vfs *vfsp, int flag, struct cred *cr)
 243 {
 244         mntdata_t *mnt = (mntdata_t *)vfsp->vfs_data;
 245         vnode_t *vp = MTOV(&mnt->mnt_node);
 246 
 247         if (secpolicy_fs_unmount(cr, vfsp) != 0)
 248                 return (EPERM);
 249 
 250         /*
 251          * Ensure that no /mnttab vnodes are in use on this mount point.
 252          */
 253         mutex_enter(&vp->v_lock);
 254         if (vp->v_count > 1 || mnt->mnt_nopen > 0) {
 255                 mutex_exit(&vp->v_lock);
 256                 return (EBUSY);
 257         }
 258 
 259         mutex_exit(&vp->v_lock);
 260         zone_rele_ref(&mnt->mnt_zone_ref, ZONE_REF_MNTFS);
 261         vn_invalid(vp);
 262         vn_free(vp);
 263         kmem_free(mnt, sizeof (*mnt));
 264         return (0);
 265 }
 266 
 267 /* ARGSUSED */
 268 static int
 269 mntroot(struct vfs *vfsp, struct vnode **vpp)
 270 {
 271         mntnode_t *mnp = &((mntdata_t *)vfsp->vfs_data)->mnt_node;
 272         struct vnode *vp = MTOV(mnp);
 273 
 274         VN_HOLD(vp);
 275         *vpp = vp;
 276         return (0);
 277 }
 278 
 279 static int
 280 mntstatvfs(struct vfs *vfsp, struct statvfs64 *sp)
 281 {
 282         dev32_t d32;
 283 
 284         bzero((caddr_t)sp, sizeof (*sp));
 285         sp->f_bsize  = DEV_BSIZE;
 286         sp->f_frsize = DEV_BSIZE;
 287         sp->f_blocks = (fsblkcnt64_t)0;
 288         sp->f_bfree  = (fsblkcnt64_t)0;
 289         sp->f_bavail = (fsblkcnt64_t)0;
 290         sp->f_files  = (fsfilcnt64_t)1;
 291         sp->f_ffree  = (fsfilcnt64_t)0;
 292         sp->f_favail = (fsfilcnt64_t)0;
 293         (void) cmpldev(&d32, vfsp->vfs_dev);
 294         sp->f_fsid   = d32;
 295         (void) strcpy(sp->f_basetype, vfssw[mntfstype].vsw_name);
 296         sp->f_flag = vf_to_stf(vfsp->vfs_flag);
 297         sp->f_namemax = 64;          /* quite arbitrary */
 298         bzero(sp->f_fstr, sizeof (sp->f_fstr));
 299         (void) strcpy(sp->f_fstr, "/mnttab");
 300         (void) strcpy(&sp->f_fstr[8], "/mnttab");
 301         return (0);
 302 }