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 };