lofs_vfsops.c revision b431137cd272e49097db14ab8b85efbbe92a1d76
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * CDDL HEADER START
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson *
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * The contents of this file are subject to the terms of the
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Common Development and Distribution License, Version 1.0 only
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * (the "License"). You may not use this file except in compliance
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * with the License.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson *
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * or http://www.opensolaris.org/os/licensing.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * See the License for the specific language governing permissions
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * and limitations under the License.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson *
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * When distributing Covered Code, include this CDDL HEADER in each
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * If applicable, add the following below this CDDL HEADER, with the
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * fields enclosed by brackets "[]" replaced with your own identifying
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * information: Portions Copyright [yyyy] [name of copyright owner]
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson *
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * CDDL HEADER END
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Use is subject to license terms.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#pragma ident "%Z%%M% %I% %E% SMI"
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/param.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/errno.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/vfs.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/vnode.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/uio.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/pathname.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/kmem.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/cred.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/statvfs.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/fs/lofs_info.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/fs/lofs_node.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/mount.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/mntent.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/mkdev.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/sysmacros.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/systm.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/cmn_err.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/policy.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include "fs/fs_subr.h"
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * This is the loadable module wrapper.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson#include <sys/modctl.h>
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic mntopts_t lofs_mntopts;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic int lofsinit(int, char *);
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic vfsdef_t vfw = {
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson VFSDEF_VERSION,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson "lofs",
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson lofsinit,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson VSW_HASPROTO,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson &lofs_mntopts
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson};
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Stuff needed to support "zonedevfs" mode.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic major_t lofs_major;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic minor_t lofs_minor;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic kmutex_t lofs_minor_lock;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * LOFS mount options table
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL };
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic char *noxattr_cancel[] = { MNTOPT_XATTR, NULL };
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic char *zonedevfs_cancel[] = { MNTOPT_LOFS_NOZONEDEVFS, NULL };
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic char *nozonedevfs_cancel[] = { MNTOPT_LOFS_ZONEDEVFS, NULL };
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic char *sub_cancel[] = { MNTOPT_LOFS_NOSUB, NULL };
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic char *nosub_cancel[] = { MNTOPT_LOFS_SUB, NULL };
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic mntopt_t mntopts[] = {
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * option name cancel option default arg flags
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * private data
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson { MNTOPT_XATTR, xattr_cancel, NULL, 0,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson (void *)0 },
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson { MNTOPT_NOXATTR, noxattr_cancel, NULL, 0,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson (void *)0 },
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson { MNTOPT_LOFS_ZONEDEVFS, zonedevfs_cancel, NULL, 0,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson (void *)0 },
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson { MNTOPT_LOFS_NOZONEDEVFS, nozonedevfs_cancel, NULL, 0,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson (void *)0 },
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson { MNTOPT_LOFS_SUB, sub_cancel, NULL, 0,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson (void *)0 },
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson { MNTOPT_LOFS_NOSUB, nosub_cancel, NULL, 0,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson (void *)0 },
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson};
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic mntopts_t lofs_mntopts = {
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson sizeof (mntopts) / sizeof (mntopt_t),
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson mntopts
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson};
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Module linkage information for the kernel.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic struct modlfs modlfs = {
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson &mod_fsops, "filesystem for lofs", &vfw
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson};
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic struct modlinkage modlinkage = {
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson MODREV_1, (void *)&modlfs, NULL
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson};
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * This is the module initialization routine.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonint
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson_init()
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson{
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson int status;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson lofs_subrinit();
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson status = mod_install(&modlinkage);
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson if (status != 0) {
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson /*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Cleanup previously initialized work.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson lofs_subrfini();
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson }
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson return (status);
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson}
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Don't allow the lofs module to be unloaded for now.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * There is a memory leak if it gets unloaded.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonint
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson_fini()
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson{
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson return (EBUSY);
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson}
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonint
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson_info(struct modinfo *modinfop)
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson{
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson return (mod_info(&modlinkage, modinfop));
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson}
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonstatic int lofsfstype;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlsonvfsops_t *lo_vfsops;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson/*
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * lo mount vfsop
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson * Set up mount info record and attach it to vfs struct.
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson */
ab32bdf2f746488f918233b2d8cabd5835efe9f3James Carlson/*ARGSUSED*/
ab32bdf2f746488f918233b2d8cabd5835efe9f3James Carlsonstatic int
ab32bdf2f746488f918233b2d8cabd5835efe9f3James Carlsonlo_mount(struct vfs *vfsp,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson struct vnode *vp,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson struct mounta *uap,
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson struct cred *cr)
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson{
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson int error;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson struct vnode *srootvp = NULL; /* the server's root */
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson struct vnode *realrootvp;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson struct loinfo *li;
b00044a2eb43864b8718585d21949611a2ee59efJames Carlson int is_zonedevfs = 0;
int nodev;
nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL);
if ((error = secpolicy_fs_mount(cr, vp, vfsp)) != 0)
return (EPERM);
/*
* Loopback devices which get "nodevices" added can be done without
* "nodevices" set because we cannot import devices into a zone
* with loopback. Note that we have all zone privileges when
* this happens; if not, we'd have gotten "nosuid".
*/
if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY);
/*
* We must ensure that only the global zone applies the 'zonedevfs'
* option; we don't want non-global zones to be able to establish
* lofs mounts using the special dev_t we use to ensure that the
* contents of a zone's /dev cannot be victim to link(2) or rename(2).
* See below, where we set all of this up.
*
* Since this is more like a privilege check, we use crgetzoneid(cr)
* instead of getzoneid().
*/
is_zonedevfs = vfs_optionisset(vfsp, MNTOPT_LOFS_ZONEDEVFS, NULL);
if (crgetzoneid(cr) != GLOBAL_ZONEID && is_zonedevfs)
return (EPERM);
mutex_enter(&vp->v_lock);
if (!(uap->flags & MS_OVERLAY) &&
(vp->v_count != 1 || (vp->v_flag & VROOT))) {
mutex_exit(&vp->v_lock);
return (EBUSY);
}
mutex_exit(&vp->v_lock);
/*
* Find real root, and make vfs point to real vfs
*/
if (error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ?
UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP,
&realrootvp))
return (error);
/*
* realrootvp may be an AUTOFS node, in which case we
* perform a VOP_ACCESS() to trigger the mount of the
* intended filesystem, so we loopback mount the intended
* filesystem instead of the AUTOFS filesystem.
*/
(void) VOP_ACCESS(realrootvp, 0, 0, cr);
/*
* We're interested in the top most filesystem.
* This is specially important when uap->spec is a trigger
* AUTOFS node, since we're really interested in mounting the
* filesystem AUTOFS mounted as result of the VOP_ACCESS()
* call not the AUTOFS node itself.
*/
if (vn_mountedvfs(realrootvp) != NULL) {
if (error = traverse(&realrootvp)) {
VN_RELE(realrootvp);
return (error);
}
}
/*
* Allocate a vfs info struct and attach it
*/
li = kmem_zalloc(sizeof (struct loinfo), KM_SLEEP);
li->li_realvfs = realrootvp->v_vfsp;
li->li_mountvfs = vfsp;
/*
* Set mount flags to be inherited by loopback vfs's
*/
if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
li->li_mflag |= VFS_RDONLY;
}
if (vfs_optionisset(vfsp, MNTOPT_NOSUID, NULL)) {
li->li_mflag |= (VFS_NOSETUID|VFS_NODEVICES);
}
if (vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL)) {
li->li_mflag |= VFS_NODEVICES;
}
if (vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL)) {
li->li_mflag |= VFS_NOSETUID;
}
/*
* Permissive flags are added to the "deny" bitmap.
*/
if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) {
li->li_dflag |= VFS_XATTR;
}
if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) {
li->li_dflag |= VFS_NBMAND;
}
/*
* Propagate inheritable mount flags from the real vfs.
*/
if ((li->li_realvfs->vfs_flag & VFS_RDONLY) &&
!vfs_optionisset(vfsp, MNTOPT_RO, NULL))
vfs_setmntopt(vfsp, MNTOPT_RO, NULL,
VFS_NODISPLAY);
if ((li->li_realvfs->vfs_flag & VFS_NOSETUID) &&
!vfs_optionisset(vfsp, MNTOPT_NOSETUID, NULL))
vfs_setmntopt(vfsp, MNTOPT_NOSETUID, NULL,
VFS_NODISPLAY);
if ((li->li_realvfs->vfs_flag & VFS_NODEVICES) &&
!vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
vfs_setmntopt(vfsp, MNTOPT_NODEVICES, NULL,
VFS_NODISPLAY);
/*
* Permissive flags such as VFS_XATTR, as opposed to restrictive flags
* such as VFS_RDONLY, are handled differently. An explicit
* MNTOPT_NOXATTR should override the underlying filesystem's VFS_XATTR.
*/
if ((li->li_realvfs->vfs_flag & VFS_XATTR) &&
!vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL) &&
!vfs_optionisset(vfsp, MNTOPT_XATTR, NULL))
vfs_setmntopt(vfsp, MNTOPT_XATTR, NULL,
VFS_NODISPLAY);
if ((li->li_realvfs->vfs_flag & VFS_NBMAND) &&
!vfs_optionisset(vfsp, MNTOPT_NBMAND, NULL) &&
!vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL))
vfs_setmntopt(vfsp, MNTOPT_NBMAND, NULL,
VFS_NODISPLAY);
li->li_refct = 0;
vfsp->vfs_data = (caddr_t)li;
vfsp->vfs_bcount = 0;
vfsp->vfs_fstype = lofsfstype;
vfsp->vfs_bsize = li->li_realvfs->vfs_bsize;
/*
* Test to see if we need to be in "zone /dev" mode. In zonedevfs
* mode, we pull a nasty trick; we make sure that the lofs dev_t does
* *not* reflect the underlying device, so that no renames or links
* can occur to or from the /dev hierarchy.
*/
if (is_zonedevfs) {
dev_t dev;
mutex_enter(&lofs_minor_lock);
do {
lofs_minor = (lofs_minor + 1) & MAXMIN32;
dev = makedevice(lofs_major, lofs_minor);
} while (vfs_devismounted(dev));
mutex_exit(&lofs_minor_lock);
vfsp->vfs_dev = dev;
vfs_make_fsid(&vfsp->vfs_fsid, dev, lofsfstype);
li->li_flag |= LO_ZONEDEVFS;
} else {
vfsp->vfs_dev = li->li_realvfs->vfs_dev;
vfsp->vfs_fsid.val[0] = li->li_realvfs->vfs_fsid.val[0];
vfsp->vfs_fsid.val[1] = li->li_realvfs->vfs_fsid.val[1];
}
if (vfs_optionisset(vfsp, MNTOPT_LOFS_NOSUB, NULL)) {
li->li_flag |= LO_NOSUB;
}
/*
* Setup the hashtable. If the root of this mount isn't a directory,
* there's no point in allocating a large hashtable. A table with one
* bucket is sufficient.
*/
if (realrootvp->v_type != VDIR)
lsetup(li, 1);
else
lsetup(li, 0);
/*
* Make the root vnode
*/
srootvp = makelonode(realrootvp, li, 0);
srootvp->v_flag |= VROOT;
li->li_rootvp = srootvp;
#ifdef LODEBUG
lo_dprint(4, "lo_mount: vfs %p realvfs %p root %p realroot %p li %p\n",
vfsp, li->li_realvfs, srootvp, realrootvp, li);
#endif
return (0);
}
/*
* Undo loopback mount
*/
static int
lo_unmount(struct vfs *vfsp, int flag, struct cred *cr)
{
struct loinfo *li;
if (secpolicy_fs_unmount(cr, vfsp) != 0)
return (EPERM);
/*
* Forced unmount is not supported by this file system
* and thus, ENOTSUP, is being returned.
*/
if (flag & MS_FORCE)
return (ENOTSUP);
li = vtoli(vfsp);
#ifdef LODEBUG
lo_dprint(4, "lo_unmount(%p) li %p\n", vfsp, li);
#endif
if (li->li_refct != 1 || li->li_rootvp->v_count != 1) {
#ifdef LODEBUG
lo_dprint(4, "refct %d v_ct %d\n", li->li_refct,
li->li_rootvp->v_count);
#endif
return (EBUSY);
}
VN_RELE(li->li_rootvp);
return (0);
}
/*
* Find root of lofs mount.
*/
static int
lo_root(struct vfs *vfsp, struct vnode **vpp)
{
*vpp = vtoli(vfsp)->li_rootvp;
#ifdef LODEBUG
lo_dprint(4, "lo_root(0x%p) = %p\n", vfsp, *vpp);
#endif
/*
* If the root of the filesystem is a special file, return the specvp
* version of the vnode. We don't save the specvp vnode in our
* hashtable since that's exclusively for lnodes.
*/
if (IS_DEVVP(*vpp)) {
struct vnode *svp;
svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, kcred);
if (svp == NULL)
return (ENOSYS);
*vpp = svp;
} else {
VN_HOLD(*vpp);
}
return (0);
}
/*
* Get file system statistics.
*/
static int
lo_statvfs(register struct vfs *vfsp, struct statvfs64 *sbp)
{
vnode_t *realrootvp;
#ifdef LODEBUG
lo_dprint(4, "lostatvfs %p\n", vfsp);
#endif
/*
* Using realrootvp->v_vfsp (instead of the realvfsp that was
* cached) is necessary to make lofs work woth forced UFS unmounts.
* In the case of a forced unmount, UFS stores a set of dummy vfsops
* in all the (i)vnodes in the filesystem. The dummy ops simply
* returns back EIO.
*/
(void) lo_realvfs(vfsp, &realrootvp);
if (realrootvp != NULL)
return (VFS_STATVFS(realrootvp->v_vfsp, sbp));
else
return (EIO);
}
/*
* LOFS doesn't have any data or metadata to flush, pending I/O on the
* underlying filesystem will be flushed when such filesystem is synched.
*/
/* ARGSUSED */
static int
lo_sync(struct vfs *vfsp,
short flag,
struct cred *cr)
{
#ifdef LODEBUG
lo_dprint(4, "lo_sync: %p\n", vfsp);
#endif
return (0);
}
/*
* Obtain the vnode from the underlying filesystem.
*/
static int
lo_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
{
vnode_t *realrootvp;
#ifdef LODEBUG
lo_dprint(4, "lo_vget: %p\n", vfsp);
#endif
(void) lo_realvfs(vfsp, &realrootvp);
if (realrootvp != NULL)
return (VFS_VGET(realrootvp->v_vfsp, vpp, fidp));
else
return (EIO);
}
/*
* Free mount-specific data.
*/
static void
lo_freevfs(struct vfs *vfsp)
{
struct loinfo *li = vtoli(vfsp);
ldestroy(li);
kmem_free(li, sizeof (struct loinfo));
}
static int
lofsinit(int fstyp, char *name)
{
static const fs_operation_def_t lo_vfsops_template[] = {
VFSNAME_MOUNT, lo_mount,
VFSNAME_UNMOUNT, lo_unmount,
VFSNAME_ROOT, lo_root,
VFSNAME_STATVFS, lo_statvfs,
VFSNAME_SYNC, (fs_generic_func_p) lo_sync,
VFSNAME_VGET, lo_vget,
VFSNAME_FREEVFS, (fs_generic_func_p) lo_freevfs,
NULL, NULL
};
int error;
error = vfs_setfsops(fstyp, lo_vfsops_template, &lo_vfsops);
if (error != 0) {
cmn_err(CE_WARN, "lofsinit: bad vfs ops template");
return (error);
}
error = vn_make_ops(name, lo_vnodeops_template, &lo_vnodeops);
if (error != 0) {
(void) vfs_freevfsops_by_type(fstyp);
cmn_err(CE_WARN, "lofsinit: bad vnode ops template");
return (error);
}
lofsfstype = fstyp;
if ((lofs_major = getudev()) == (major_t)-1) {
(void) vfs_freevfsops_by_type(fstyp);
cmn_err(CE_WARN, "lofsinit: Can't get unique device number.");
return (ENXIO);
}
lofs_minor = 0;
mutex_init(&lofs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
return (0);
}