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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <sys/atomic.h>
  26 #include <sys/cmn_err.h>
  27 #include <sys/errno.h>
  28 #include <sys/mount.h>
  29 #include <sys/objfs.h>
  30 #include <sys/objfs_impl.h>
  31 #include <sys/vfs_opreg.h>
  32 #include <sys/policy.h>
  33 #include <sys/sunddi.h>
  34 #include <sys/sysmacros.h>
  35 #include <sys/systm.h>
  36 
  37 /*
  38  * Kernel object filesystem.
  39  *
  40  * This is a pseudo filesystem which exports information about currently loaded
  41  * kernel objects.  The root directory contains one directory for each loaded
  42  * object, indexed by module name.  Within each object directory is an ELF file,
  43  * 'object', that contains information about the currently loaded module.
  44  *
  45  * This file contains functions that interact with the VFS layer.  Each
  46  * filesystem element is represented by a a different node.
  47  *
  48  *      /               objfs_rootnode_t        objfs_root.c
  49  *      /<obj>            objfs_odirnode_t        objfs_odir.c
  50  *      /<obj>/object     objfs_datanode_t        objfs_data.c
  51  *
  52  * In addition, some common routines are found in the 'objfs_common.c' file.
  53  */
  54 
  55 vnodeops_t *objfs_ops_root;
  56 vnodeops_t *objfs_ops_odir;
  57 vnodeops_t *objfs_ops_data;
  58 
  59 static const fs_operation_def_t objfs_vfstops[];
  60 static gfs_opsvec_t objfs_opsvec[];
  61 
  62 static int objfs_init(int, char *);
  63 
  64 /*
  65  * Module linkage
  66  */
  67 static mntopts_t objfs_mntopts = {
  68         0,
  69         NULL
  70 };
  71 
  72 static vfsdef_t vfw = {
  73         VFSDEF_VERSION,
  74         "objfs",
  75         objfs_init,
  76         VSW_HASPROTO | VSW_ZMOUNT,
  77         &objfs_mntopts,
  78 };
  79 
  80 extern struct mod_ops mod_fsops;
  81 
  82 static struct modlfs modlfs = {
  83         &mod_fsops, "kernel object filesystem", &vfw
  84 };
  85 
  86 static struct modlinkage modlinkage = {
  87         MODREV_1, (void *)&modlfs, NULL
  88 };
  89 
  90 int
  91 _init(void)
  92 {
  93         return (mod_install(&modlinkage));
  94 }
  95 
  96 int
  97 _info(struct modinfo *modinfop)
  98 {
  99         return (mod_info(&modlinkage, modinfop));
 100 }
 101 
 102 int
 103 _fini(void)
 104 {
 105         /*
 106          * The object filesystem cannot be unloaded.
 107          */
 108         return (EBUSY);
 109 }
 110 
 111 /*
 112  * Filesystem initialization.
 113  */
 114 
 115 static int objfs_fstype;
 116 static major_t objfs_major;
 117 static minor_t objfs_minor;
 118 
 119 static gfs_opsvec_t objfs_opsvec[] = {
 120         { "objfs root directory", objfs_tops_root, &objfs_ops_root },
 121         { "objfs object directory", objfs_tops_odir, &objfs_ops_odir },
 122         { "objfs data file", objfs_tops_data, &objfs_ops_data },
 123         { NULL }
 124 };
 125 
 126 /* ARGSUSED */
 127 static int
 128 objfs_init(int fstype, char *name)
 129 {
 130         vfsops_t *vfsops;
 131         int error;
 132 
 133         objfs_fstype = fstype;
 134         if (error = vfs_setfsops(fstype, objfs_vfstops, &vfsops)) {
 135                 cmn_err(CE_WARN, "objfs_init: bad vfs ops template");
 136                 return (error);
 137         }
 138 
 139         if (error = gfs_make_opsvec(objfs_opsvec)) {
 140                 (void) vfs_freevfsops(vfsops);
 141                 return (error);
 142         }
 143 
 144         if ((objfs_major = getudev()) == (major_t)-1) {
 145                 cmn_err(CE_WARN, "objfs_init: can't get unique device number");
 146                 objfs_major = 0;
 147         }
 148 
 149         objfs_data_init();
 150 
 151         return (0);
 152 }
 153 
 154 /*
 155  * VFS entry points
 156  */
 157 static int
 158 objfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 159 {
 160         objfs_vfs_t *data;
 161         dev_t dev;
 162 
 163         if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
 164                 return (EPERM);
 165 
 166         if (mvp->v_type != VDIR)
 167                 return (ENOTDIR);
 168 
 169         if ((uap->flags & MS_OVERLAY) == 0 &&
 170             (mvp->v_count > 1 || (mvp->v_flag & VROOT)))
 171                 return (EBUSY);
 172 
 173         data = kmem_alloc(sizeof (objfs_vfs_t), KM_SLEEP);
 174 
 175         /*
 176          * Initialize vfs fields
 177          */
 178         vfsp->vfs_bsize = DEV_BSIZE;
 179         vfsp->vfs_fstype = objfs_fstype;
 180         do {
 181                 dev = makedevice(objfs_major,
 182                     atomic_inc_32_nv(&objfs_minor) & L_MAXMIN32);
 183         } while (vfs_devismounted(dev));
 184         vfs_make_fsid(&vfsp->vfs_fsid, dev, objfs_fstype);
 185         vfsp->vfs_data = data;
 186         vfsp->vfs_dev = dev;
 187 
 188         /*
 189          * Create root
 190          */
 191         data->objfs_vfs_root = objfs_create_root(vfsp);
 192 
 193         return (0);
 194 }
 195 
 196 static int
 197 objfs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
 198 {
 199         objfs_vfs_t *data;
 200 
 201         if (secpolicy_fs_unmount(cr, vfsp) != 0)
 202                 return (EPERM);
 203 
 204         /*
 205          * We do not currently support forced unmounts
 206          */
 207         if (flag & MS_FORCE)
 208                 return (ENOTSUP);
 209 
 210         /*
 211          * We should never have a reference count of less than 2: one for the
 212          * caller, one for the root vnode.
 213          */
 214         ASSERT(vfsp->vfs_count >= 2);
 215 
 216         /*
 217          * Any active vnodes will result in a hold on the root vnode
 218          */
 219         data = vfsp->vfs_data;
 220         if (data->objfs_vfs_root->v_count > 1)
 221                 return (EBUSY);
 222 
 223         /*
 224          * Release the last hold on the root vnode
 225          */
 226         VN_RELE(data->objfs_vfs_root);
 227 
 228         kmem_free(data, sizeof (objfs_vfs_t));
 229 
 230         return (0);
 231 }
 232 
 233 static int
 234 objfs_root(vfs_t *vfsp, vnode_t **vpp)
 235 {
 236         objfs_vfs_t *data = vfsp->vfs_data;
 237 
 238         *vpp = data->objfs_vfs_root;
 239         VN_HOLD(*vpp);
 240 
 241         return (0);
 242 }
 243 
 244 static int
 245 objfs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
 246 {
 247         dev32_t d32;
 248         int total = objfs_nobjs();
 249 
 250         bzero(sp, sizeof (*sp));
 251         sp->f_bsize = DEV_BSIZE;
 252         sp->f_frsize = DEV_BSIZE;
 253         sp->f_files = total;
 254         sp->f_ffree = sp->f_favail = INT_MAX - total;
 255         (void) cmpldev(&d32, vfsp->vfs_dev);
 256         sp->f_fsid = d32;
 257         (void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
 258             sizeof (sp->f_basetype));
 259         sp->f_flag = vf_to_stf(vfsp->vfs_flag);
 260         sp->f_namemax = OBJFS_NAME_MAX;
 261         (void) strlcpy(sp->f_fstr, "object", sizeof (sp->f_fstr));
 262 
 263         return (0);
 264 }
 265 
 266 static const fs_operation_def_t objfs_vfstops[] = {
 267         { VFSNAME_MOUNT,        { .vfs_mount = objfs_mount } },
 268         { VFSNAME_UNMOUNT,      { .vfs_unmount = objfs_unmount } },
 269         { VFSNAME_ROOT,         { .vfs_root = objfs_root } },
 270         { VFSNAME_STATVFS,      { .vfs_statvfs = objfs_statvfs } },
 271         { NULL }
 272 };