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