#include <sys/param.h>
#include <sys/errno.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/pathname.h>
#include <sys/kmem.h>
#include <sys/cred.h>
#include <sys/statvfs.h>
#include <sys/fs/lofs_info.h>
#include <sys/fs/lofs_node.h>
#include <sys/mount.h>
#include <sys/mntent.h>
#include <sys/mkdev.h>
#include <sys/priv.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/policy.h>
#include <sys/tsol/label.h>
#include "fs/fs_subr.h"
#include <sys/modctl.h>
static mntopts_t lofs_mntopts;
static int lofsinit(int, char *);
static vfsdef_t vfw = {
VFSDEF_VERSION,
"lofs",
lofsinit,
VSW_HASPROTO|VSW_STATS|VSW_ZMOUNT,
&lofs_mntopts
};
static char *xattr_cancel[] = { MNTOPT_NOXATTR, NULL };
static char *noxattr_cancel[] = { MNTOPT_XATTR, NULL };
static char *sub_cancel[] = { MNTOPT_LOFS_NOSUB, NULL };
static char *nosub_cancel[] = { MNTOPT_LOFS_SUB, NULL };
static mntopt_t mntopts[] = {
{ MNTOPT_XATTR, xattr_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_NOXATTR, noxattr_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_LOFS_SUB, sub_cancel, NULL, 0,
(void *)0 },
{ MNTOPT_LOFS_NOSUB, nosub_cancel, NULL, 0,
(void *)0 },
};
static mntopts_t lofs_mntopts = {
sizeof (mntopts) / sizeof (mntopt_t),
mntopts
};
static struct modlfs modlfs = {
&mod_fsops, "filesystem for lofs", &vfw
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlfs, NULL
};
int
_init(void)
{
int status;
lofs_subrinit();
status = mod_install(&modlinkage);
if (status != 0) {
lofs_subrfini();
}
return (status);
}
int
_fini(void)
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static int lofsfstype;
vfsops_t *lo_vfsops;
static int
lo_mount(struct vfs *vfsp, struct vnode *vp, struct mounta *uap,
struct cred *cr)
{
int error;
struct vnode *srootvp = NULL;
struct vnode *realrootvp;
struct loinfo *li;
int nodev;
nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL);
if ((error = secpolicy_fs_mount(cr, vp, vfsp)) != 0)
return (EPERM);
if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY);
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);
if (error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ?
UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, &realrootvp))
return (error);
if (is_system_labeled() && crgetzoneid(cr) == GLOBAL_ZONEID) {
char specname[MAXPATHLEN];
zone_t *from_zptr;
zone_t *to_zptr;
if (vnodetopath(NULL, realrootvp, specname,
sizeof (specname), CRED()) != 0) {
VN_RELE(realrootvp);
return (EACCES);
}
from_zptr = zone_find_by_path(specname);
to_zptr = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
if (from_zptr != to_zptr &&
!(to_zptr->zone_flags & ZF_IS_SCRATCH)) {
if (from_zptr->zone_id == GLOBAL_ZONEID) {
vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
} else {
if (to_zptr->zone_id == GLOBAL_ZONEID &&
getpflags(NET_MAC_AWARE, cr) != 0) {
} else if (bldominates(
label2bslabel(to_zptr->zone_slabel),
label2bslabel(from_zptr->zone_slabel))) {
vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
} else {
VN_RELE(realrootvp);
zone_rele(to_zptr);
zone_rele(from_zptr);
return (EACCES);
}
}
}
zone_rele(to_zptr);
zone_rele(from_zptr);
}
if ((error = VOP_ACCESS(realrootvp, 0, 0, cr, NULL)) != 0) {
VN_RELE(realrootvp);
return (error);
}
if (vn_mountedvfs(realrootvp) != NULL) {
if (error = traverse(&realrootvp)) {
VN_RELE(realrootvp);
return (error);
}
}
li = kmem_zalloc(sizeof (struct loinfo), KM_SLEEP);
li->li_realvfs = realrootvp->v_vfsp;
li->li_mountvfs = vfsp;
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;
}
if (vfs_optionisset(vfsp, MNTOPT_NOXATTR, NULL)) {
li->li_dflag |= VFS_XATTR;
}
if (vfs_optionisset(vfsp, MNTOPT_NONBMAND, NULL)) {
li->li_dflag |= VFS_NBMAND;
}
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);
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;
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;
}
vfs_propagate_features(li->li_realvfs, vfsp);
if (realrootvp->v_type != VDIR)
lsetup(li, 1);
else
lsetup(li, 0);
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);
}
static int
lo_unmount(struct vfs *vfsp, int flag, struct cred *cr)
{
struct loinfo *li;
if (secpolicy_fs_unmount(cr, vfsp) != 0)
return (EPERM);
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);
}
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 (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);
}
static int
lo_statvfs(register struct vfs *vfsp, struct statvfs64 *sbp)
{
vnode_t *realrootvp;
#ifdef LODEBUG
lo_dprint(4, "lostatvfs %p\n", vfsp);
#endif
(void) lo_realvfs(vfsp, &realrootvp);
if (realrootvp != NULL)
return (VFS_STATVFS(realrootvp->v_vfsp, sbp));
else
return (EIO);
}
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);
}
static int
lo_syncfs(vfs_t *vfsp, uint64_t flags, cred_t *cr)
{
vfs_t *realvfs;
#ifdef LODEBUG
lo_dprint(4, "lo_syncfs: %p\n", vfsp);
#endif
realvfs = lo_realvfs(vfsp, NULL);
if (realvfs != NULL) {
return (VFS_SYNCFS(realvfs, flags, cr));
} else {
return (EIO);
}
}
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);
}
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, { .vfs_mount = lo_mount },
VFSNAME_UNMOUNT, { .vfs_unmount = lo_unmount },
VFSNAME_ROOT, { .vfs_root = lo_root },
VFSNAME_STATVFS, { .vfs_statvfs = lo_statvfs },
VFSNAME_SYNC, { .vfs_sync = lo_sync },
VFSNAME_VGET, { .vfs_vget = lo_vget },
VFSNAME_FREEVFS, { .vfs_freevfs = lo_freevfs },
VFSNAME_SYNCFS, { .vfs_syncfs = lo_syncfs },
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;
return (0);
}