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