/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include <sys/atomic.h> #include <sys/cmn_err.h> #include <sys/errno.h> #include <sys/mount.h> #include <sharefs/sharefs.h> #include <sys/vfs_opreg.h> #include <sys/policy.h> #include <sys/sunddi.h> #include <sys/sysmacros.h> #include <sys/systm.h> #include <sys/mntent.h> #include <sys/vfs.h> /* * Kernel sharetab filesystem. * * This is a pseudo filesystem which exports information about shares currently * in kernel memory. The only element of the pseudo filesystem is a file. * * This file contains functions that interact with the VFS layer. * * sharetab sharefs_datanode_t sharefs.c * */ vnodeops_t *sharefs_ops_data; static const fs_operation_def_t sharefs_vfstops[]; static gfs_opsvec_t sharefs_opsvec[]; static int sharefs_init(int, char *); /* * The sharefs system call. */ static struct sysent sharefs_sysent = { 3, SE_32RVAL1 | SE_ARGC | SE_NOUNLOAD, sharefs }; static struct modlsys modlsys = { &mod_syscallops, "sharefs syscall", &sharefs_sysent }; #ifdef _SYSCALL32_IMPL static struct modlsys modlsys32 = { &mod_syscallops32, "sharefs syscall (32-bit)", &sharefs_sysent }; #endif /* _SYSCALL32_IMPL */ /* * Module linkage */ static mntopts_t sharefs_mntopts = { 0, NULL }; static vfsdef_t vfw = { VFSDEF_VERSION, "sharefs", sharefs_init, VSW_HASPROTO, &sharefs_mntopts, }; extern struct mod_ops mod_fsops; static struct modlfs modlfs = { &mod_fsops, "sharetab filesystem", &vfw }; static struct modlinkage modlinkage = { MODREV_1, &modlfs, &modlsys, #ifdef _SYSCALL32_IMPL &modlsys32, #endif NULL }; int _init(void) { return (mod_install(&modlinkage)); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } int _fini(void) { /* * The sharetab filesystem cannot be unloaded. */ return (EBUSY); } /* * Filesystem initialization. */ static int sharefs_fstype; static major_t sharefs_major; static minor_t sharefs_minor; static gfs_opsvec_t sharefs_opsvec[] = { { "sharefs sharetab file", sharefs_tops_data, &sharefs_ops_data }, { NULL } }; /* ARGSUSED */ static int sharefs_init(int fstype, char *name) { vfsops_t *vfsops; int error; sharefs_fstype = fstype; if (error = vfs_setfsops(fstype, sharefs_vfstops, &vfsops)) { cmn_err(CE_WARN, "sharefs_init: bad vfs ops template"); return (error); } if (error = gfs_make_opsvec(sharefs_opsvec)) { (void) vfs_freevfsops(vfsops); return (error); } if ((sharefs_major = getudev()) == (major_t)-1) { cmn_err(CE_WARN, "sharefs_init: can't get unique device number"); sharefs_major = 0; } sharefs_sharetab_init(); return (0); } /* * VFS entry points */ static int sharefs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) { sharefs_vfs_t *data; dev_t dev; if (secpolicy_fs_mount(cr, mvp, vfsp) != 0) return (EPERM); if ((uap->flags & MS_OVERLAY) == 0 && (mvp->v_count > 1 || (mvp->v_flag & VROOT))) return (EBUSY); data = kmem_alloc(sizeof (sharefs_vfs_t), KM_SLEEP); /* * Initialize vfs fields */ vfsp->vfs_bsize = DEV_BSIZE; vfsp->vfs_fstype = sharefs_fstype; do { dev = makedevice(sharefs_major, atomic_add_32_nv(&sharefs_minor, 1) & L_MAXMIN32); } while (vfs_devismounted(dev)); vfs_make_fsid(&vfsp->vfs_fsid, dev, sharefs_fstype); vfsp->vfs_data = data; vfsp->vfs_dev = dev; /* * Create root */ data->sharefs_vfs_root = sharefs_create_root_file(vfsp); return (0); } static int sharefs_unmount(vfs_t *vfsp, int flag, struct cred *cr) { sharefs_vfs_t *data; if (secpolicy_fs_unmount(cr, vfsp) != 0) return (EPERM); /* * We do not currently support forced unmounts */ if (flag & MS_FORCE) return (ENOTSUP); /* * We should never have a reference count of less than 2: one for the * caller, one for the root vnode. */ ASSERT(vfsp->vfs_count >= 2); /* * Any active vnodes will result in a hold on the root vnode */ data = vfsp->vfs_data; if (data->sharefs_vfs_root->v_count > 1) return (EBUSY); /* * Only allow an unmount iff there are no entries in memory. */ rw_enter(&sharetab_lock, RW_READER); if (sharetab_size != 0) { rw_exit(&sharetab_lock); return (EBUSY); } rw_exit(&sharetab_lock); /* * Release the last hold on the root vnode */ VN_RELE(data->sharefs_vfs_root); kmem_free(data, sizeof (sharefs_vfs_t)); return (0); } static int sharefs_root(vfs_t *vfsp, vnode_t **vpp) { sharefs_vfs_t *data = vfsp->vfs_data; *vpp = data->sharefs_vfs_root; VN_HOLD(*vpp); return (0); } static int sharefs_statvfs(vfs_t *vfsp, statvfs64_t *sp) { dev32_t d32; int total = 1; bzero(sp, sizeof (*sp)); sp->f_bsize = DEV_BSIZE; sp->f_frsize = DEV_BSIZE; sp->f_files = total; sp->f_ffree = sp->f_favail = INT_MAX - total; (void) cmpldev(&d32, vfsp->vfs_dev); sp->f_fsid = d32; (void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name, sizeof (sp->f_basetype)); sp->f_flag = vf_to_stf(vfsp->vfs_flag); sp->f_namemax = SHAREFS_NAME_MAX; (void) strlcpy(sp->f_fstr, "sharefs", sizeof (sp->f_fstr)); return (0); } static const fs_operation_def_t sharefs_vfstops[] = { { VFSNAME_MOUNT, { .vfs_mount = sharefs_mount } }, { VFSNAME_UNMOUNT, { .vfs_unmount = sharefs_unmount } }, { VFSNAME_ROOT, { .vfs_root = sharefs_root } }, { VFSNAME_STATVFS, { .vfs_statvfs = sharefs_statvfs } }, { NULL } };