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 }