Пример #1
0
/*
 * The nbmand mount option can be changed at mount time.
 * We can't allow it to be toggled on live file systems or incorrect
 * behavior may be seen from cifs clients
 *
 * This property isn't registered via dsl_prop_register(), but this callback
 * will be called when a file system is first mounted
 */
static void
nbmand_changed_cb(void *arg, uint64_t newval)
{
	zfsvfs_t *zfsvfs = arg;
	if (newval == FALSE) {
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND, NULL, 0);
	} else {
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NONBMAND);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NBMAND, NULL, 0);
	}
}
Пример #2
0
static void
exec_changed_cb(void *arg, uint64_t newval)
{
	zfsvfs_t *zfsvfs = arg;

	if (newval == FALSE) {
		zfsvfs->z_vfs->vfs_flag |= VFS_NOEXEC;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_EXEC);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOEXEC, NULL, 0);
	} else {
		zfsvfs->z_vfs->vfs_flag &= ~VFS_NOEXEC;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOEXEC);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_EXEC, NULL, 0);
	}
}
Пример #3
0
static void
setuid_changed_cb(void *arg, uint64_t newval)
{
	zfsvfs_t *zfsvfs = arg;

	if (newval == FALSE) {
		zfsvfs->z_vfs->vfs_flag |= VFS_NOSETUID;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_SETUID);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOSETUID, NULL, 0);
	} else {
		zfsvfs->z_vfs->vfs_flag &= ~VFS_NOSETUID;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOSETUID);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_SETUID, NULL, 0);
	}
}
Пример #4
0
static void
devices_changed_cb(void *arg, uint64_t newval)
{
	zfsvfs_t *zfsvfs = arg;

	if (newval == FALSE) {
		zfsvfs->z_vfs->vfs_flag |= VFS_NODEVICES;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_DEVICES);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NODEVICES, NULL, 0);
	} else {
		zfsvfs->z_vfs->vfs_flag &= ~VFS_NODEVICES;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NODEVICES);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_DEVICES, NULL, 0);
	}
}
Пример #5
0
static void
atime_changed_cb(void *arg, uint64_t newval)
{
	zfsvfs_t *zfsvfs = arg;

	if (newval == TRUE) {
		zfsvfs->z_atime = TRUE;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOATIME);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_ATIME, NULL, 0);
	} else {
		zfsvfs->z_atime = FALSE;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_ATIME);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOATIME, NULL, 0);
	}
}
Пример #6
0
static void
readonly_changed_cb(void *arg, uint64_t newval)
{
	zfsvfs_t *zfsvfs = arg;

	if (newval) {
		/* XXX locking on vfs_flag? */
		zfsvfs->z_vfs->vfs_flag |= VFS_RDONLY;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_RW);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_RO, NULL, 0);
	} else {
		/* XXX locking on vfs_flag? */
		zfsvfs->z_vfs->vfs_flag &= ~VFS_RDONLY;
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_RO);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_RW, NULL, 0);
	}
}
Пример #7
0
static void
xattr_changed_cb(void *arg, uint64_t newval)
{
	zfsvfs_t *zfsvfs = arg;

	if (newval == TRUE) {
		/* XXX locking on vfs_flag? */
#ifdef TODO
		zfsvfs->z_vfs->vfs_flag |= VFS_XATTR;
#endif
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_NOXATTR);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_XATTR, NULL, 0);
	} else {
		/* XXX locking on vfs_flag? */
#ifdef TODO
		zfsvfs->z_vfs->vfs_flag &= ~VFS_XATTR;
#endif
		vfs_clearmntopt(zfsvfs->z_vfs, MNTOPT_XATTR);
		vfs_setmntopt(zfsvfs->z_vfs, MNTOPT_NOXATTR, NULL, 0);
	}
}
Пример #8
0
/*
 * Close off disk quotas for a file system.
 */
int
closedq(struct ufsvfs *ufsvfsp, struct cred *cr)
{
	struct inode *qip;

	if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0)
		return (EPERM);

	rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER);

	/*
	 * Quotas are not enabled on this file system so there is
	 * nothing more to do.
	 */
	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
		rw_exit(&ufsvfsp->vfs_dqrwlock);
		return (0);
	}

	/*
	 * At this point, the quota subsystem is quiescent on this file
	 * system so we can do all the work necessary to dismantle the
	 * quota stuff.
	 */
	qip = ufsvfsp->vfs_qinod;
	if (!qip)
		return (ufs_fault(ufsvfsp->vfs_root, "closedq: NULL qip"));

	ufsvfsp->vfs_qflags = 0;	/* disable quotas */
	vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_NOQUOTA, NULL, 0);

	/*
	 * ufs_scan_inodes() depends on vfs_qinod, so we can't
	 * clear it until afterwards.
	 */
	(void) ufs_scan_inodes(0, closedq_scan_inode, ufsvfsp, ufsvfsp);

	ufsvfsp->vfs_qinod = NULL;
	rw_exit(&ufsvfsp->vfs_dqrwlock);

	/*
	 * Sync and release the quota file inode. Since we have a private
	 * pointer to the quota inode and vfs_qinod is now clear we do not
	 * need to hold vfs_dqrwlock.
	 */
	(void) TRANS_SYNCIP(qip, 0, I_SYNC, TOP_SYNCIP_CLOSEDQ);
	VN_RELE(ITOV(qip));
	return (0);
}
Пример #9
0
/*ARGSUSED*/
static int
lo_mount(struct vfs *vfsp,
         struct vnode *vp,
         struct mounta *uap,
         struct cred *cr)
{
    int error;
    struct vnode *srootvp = NULL;	/* the server's root */
    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);

    /*
     * 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);

    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);

    /*
     * Enforce MAC policy if needed.
     *
     * Loopback mounts must not allow writing up. The dominance test
     * is intended to prevent a global zone caller from accidentally
     * creating write-up conditions between two labeled zones.
     * Local zones can't violate MAC on their own without help from
     * the global zone because they can't name a pathname that
     * they don't already have.
     *
     * The special case check for the NET_MAC_AWARE process flag is
     * to support the case of the automounter in the global zone. We
     * permit automounting of local zone directories such as home
     * directories, into the global zone as required by setlabel,
     * zonecopy, and saving of desktop sessions. Such mounts are
     * trusted not to expose the contents of one zone's directories
     * to another by leaking them through the global zone.
     */
    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));

        /*
         * Special case for zone devfs: the zone for /dev will
         * incorrectly appear as the global zone since it's not
         * under the zone rootpath.  So for zone devfs check allow
         * read-write mounts.
         *
         * Second special case for scratch zones used for Live Upgrade:
         * this is used to mount the zone's root from /root to /a in
         * the scratch zone.  As with the other special case, this
         * appears to be outside of the zone because it's not under
         * the zone rootpath, which is $ZONEPATH/lu in the scratch
         * zone case.
         */

        if (from_zptr != to_zptr &&
                !(to_zptr->zone_flags & ZF_IS_SCRATCH)) {
            /*
             * We know at this point that the labels aren't equal
             * because the zone pointers aren't equal, and zones
             * can't share a label.
             *
             * If the source is the global zone then making
             * it available to a local zone must be done in
             * read-only mode as the label will become admin_low.
             *
             * If it is a mount between local zones then if
             * the current process is in the global zone and has
             * the NET_MAC_AWARE flag, then regular read-write
             * access is allowed.  If it's in some other zone, but
             * the label on the mount point dominates the original
             * source, then allow the mount as read-only
             * ("read-down").
             */
            if (from_zptr->zone_id == GLOBAL_ZONEID) {
                /* make the mount read-only */
                vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
            } else { /* cross-zone mount */
                if (to_zptr->zone_id == GLOBAL_ZONEID &&
                        /* LINTED: no consequent */
                        getpflags(NET_MAC_AWARE, cr) != 0) {
                    /* Allow the mount as read-write */
                } else if (bldominates(
                               label2bslabel(to_zptr->zone_slabel),
                               label2bslabel(from_zptr->zone_slabel))) {
                    /* make the mount read-only */
                    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);
    }

    /*
     * 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, NULL);

    /*
     * 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;

    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;
    }

    /*
     * Propagate any VFS features
     */

    vfs_propagate_features(li->li_realvfs, vfsp);

    /*
     * 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);
}
Пример #10
0
/*
 * Enable logging
 */
int
lqfs_enable(struct vnode *vp, struct fiolog *flp, cred_t *cr)
{
	int		error;
	inode_t		*ip = VTOI(vp);
	qfsvfs_t	*qfsvfsp = ip->i_qfsvfs;
	fs_lqfs_common_t	*fs = VFS_FS_PTR(qfsvfsp);
	ml_unit_t	*ul;
#ifdef LQFS_TODO_LOCKFS
	int		reclaim = 0;
	struct lockfs	lf;
	struct ulockfs	*ulp;
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LQFS_TODO_LOCKFS */
	vfs_t		*vfsp = qfsvfsp->vfs_vfs;
	uint64_t	tmp_nbytes_actual;
	char fsclean;
	sam_sblk_t	*sblk = qfsvfsp->mi.m_sbp;

	/*
	 * File system is not capable of logging.
	 */
	if (!LQFS_CAPABLE(qfsvfsp)) {
		flp->error = FIOLOG_ENOTSUP;
		error = 0;
		goto out;
	}
	if (!SAM_MAGIC_V2A_OR_HIGHER(&sblk->info.sb)) {
		cmn_err(CE_WARN, "SAM-QFS: %s: Not enabling logging, "
		    " file system is not version 2A.", qfsvfsp->mt.fi_name);
		cmn_err(CE_WARN, "\tUpgrade file system with samfsck -u "
		    "first.");
		flp->error = FIOLOG_ENOTSUP;
		error = 0;
		goto out;
	}

	if (LQFS_GET_LOGBNO(fs)) {
		error = lqfs_log_validate(qfsvfsp, flp, cr);
	}

	/*
	 * Check if logging is already enabled
	 */
	if (LQFS_GET_LOGP(qfsvfsp)) {
		flp->error = FIOLOG_ETRANS;
		/* for root ensure logging option is set */
		vfs_setmntopt(vfsp, MNTOPT_LOGGING, NULL, 0);
		error = 0;
		goto out;
	}

	/*
	 * Come back here to recheck if we had to disable the log.
	 */
recheck:
	error = 0;
	flp->error = FIOLOG_ENONE;

	/*
	 * Adjust requested log size
	 */
	flp->nbytes_actual = flp->nbytes_requested;
	if (flp->nbytes_actual == 0) {
		tmp_nbytes_actual =
		    (((uint64_t)FS_SIZE(fs)) / ldl_divisor) << FS_FSHIFT(fs);
		flp->nbytes_actual = (uint_t)MIN(tmp_nbytes_actual, INT_MAX);
	}
	flp->nbytes_actual = MAX(flp->nbytes_actual, ldl_minlogsize);
	flp->nbytes_actual = MIN(flp->nbytes_actual, ldl_maxlogsize);
	flp->nbytes_actual = blkroundup(fs, flp->nbytes_actual);

	/*
	 * logging is enabled and the log is the right size; done
	 */
	ul = LQFS_GET_LOGP(qfsvfsp);

	if (ul && LQFS_GET_LOGBNO(fs) &&
	    (flp->nbytes_actual == ul->un_requestsize)) {
		vfs_setmntopt(vfsp, MNTOPT_LOGGING, NULL, 0);
		error = 0;
		goto out;
	}

	/*
	 * Readonly file system
	 */
	if (FS_RDONLY(fs)) {
		flp->error = FIOLOG_EROFS;
		error = 0;
		goto out;
	}

#ifdef LQFS_TODO_LOCKFS
	/*
	 * File system must be write locked to enable logging
	 */
	error = qfs_fiolfss(vp, &lf);
	if (error) {
		goto out;
	}
	if (!LOCKFS_IS_ULOCK(&lf)) {
		flp->error = FIOLOG_EULOCK;
		error = 0;
		goto out;
	}
	lf.lf_lock = LOCKFS_WLOCK;
	lf.lf_flags = 0;
	lf.lf_comment = NULL;
	error = qfs_fiolfs(vp, &lf, 1);
	if (error) {
		flp->error = FIOLOG_EWLOCK;
		error = 0;
		goto out;
	}
#else
	/* QFS doesn't really support lockfs. */
#endif /* LQFS_TODO_LOCKFS */

	/*
	 * Grab appropriate locks to synchronize with the rest
	 * of the system
	 */
	vfs_lock_wait(vfsp);
#ifdef LQFS_TODO_LOCKFS
	ulp = &ufsvfsp->vfs_ulockfs;
	mutex_enter(&ulp->ul_lock);
#else
	/* QFS doesn't really support lockfs. */
#endif /* LQFS_TODO_LOCKFS */

	/*
	 * File system must be fairly consistent to enable logging
	 */
	fsclean = LQFS_GET_FS_CLEAN(fs);
	if (fsclean != FSLOG &&
	    fsclean != FSACTIVE &&
	    fsclean != FSSTABLE &&
	    fsclean != FSCLEAN) {
		flp->error = FIOLOG_ECLEAN;
		goto unlockout;
	}

#ifdef LUFS
	/*
	 * A write-locked file system is only active if there are
	 * open deleted files; so remember to set FS_RECLAIM later.
	 */
	if (LQFS_GET_FS_CLEAN(fs) == FSACTIVE) {
		reclaim = FS_RECLAIM;
	}
#else
	/* QFS doesn't have a reclaim file thread. */
#endif /* LUFS */

	/*
	 * Logging is already enabled; must be changing the log's size
	 */
	if (LQFS_GET_LOGBNO(fs) && LQFS_GET_LOGP(qfsvfsp)) {
#ifdef LQFS_TODO_LOCKFS
		/*
		 * Before we can disable logging, we must give up our
		 * lock.  As a consequence of unlocking and disabling the
		 * log, the fs structure may change.  Because of this, when
		 * disabling is complete, we will go back to recheck to
		 * repeat all of the checks that we performed to get to
		 * this point.  Disabling sets fs->fs_logbno to 0, so this
		 * will not put us into an infinite loop.
		 */
		mutex_exit(&ulp->ul_lock);
#else
		/* QFS doesn't really support lockfs. */
#endif /* LQFS_TODO_LOCKFS */
		vfs_unlock(vfsp);

#ifdef LQFS_TODO_LOCKFS
		lf.lf_lock = LOCKFS_ULOCK;
		lf.lf_flags = 0;
		error = qfs_fiolfs(vp, &lf, 1);
		if (error) {
			flp->error = FIOLOG_ENOULOCK;
			error = 0;
			goto out;
		}
#else
		/* QFS doesn't really support lockfs. */
#endif /* LQFS_TODO_LOCKFS */
		error = lqfs_disable(vp, flp);
		if (error || (flp->error != FIOLOG_ENONE)) {
			error = 0;
			goto out;
		}
		goto recheck;
	}

	error = lqfs_alloc(qfsvfsp, flp, cr);
	if (error) {
		goto errout;
	}
#ifdef LUFS
#else
	if ((error = lqfs_log_validate(qfsvfsp, flp, cr)) != 0) {
		goto errout;
	}
#endif /* LUFS */

	/*
	 * Create all of the incore structs
	 */
	error = lqfs_snarf(qfsvfsp, fs, 0);
	if (error) {
		goto errout;
	}

	/*
	 * DON'T ``GOTO ERROUT'' PAST THIS POINT
	 */

	/*
	 * Pretend we were just mounted with logging enabled
	 *	freeze and drain the file system of readers
	 *		Get the ops vector
	 *		If debug, record metadata locations with log subsystem
	 *		Start the delete thread
	 *		Start the reclaim thread, if necessary
	 *	Thaw readers
	 */
	vfs_setmntopt(vfsp, MNTOPT_LOGGING, NULL, 0);

	TRANS_DOMATAMAP(qfsvfsp);
	TRANS_MATA_MOUNT(qfsvfsp);
	TRANS_MATA_SI(qfsvfsp, fs);

#ifdef LUFS
	qfs_thread_start(&qfsvfsp->vfs_delete, qfs_thread_delete, vfsp);
	if (fs->fs_reclaim & (FS_RECLAIM|FS_RECLAIMING)) {
		fs->fs_reclaim &= ~FS_RECLAIM;
		fs->fs_reclaim |=  FS_RECLAIMING;
		qfs_thread_start(&qfsvfsp->vfs_reclaim,
		    qfs_thread_reclaim, vfsp);
	} else {
		fs->fs_reclaim |= reclaim;
	}
#else
	/* QFS doesn't have file reclaim nor i-node delete threads. */
#endif /* LUFS */

#ifdef LUFS
	mutex_exit(&ulp->ul_lock);
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LUFS */

	vfs_unlock(vfsp);

#ifdef LQFS_TODO_LOCKFS
	/*
	 * Unlock the file system
	 */
	lf.lf_lock = LOCKFS_ULOCK;
	lf.lf_flags = 0;
	error = qfs_fiolfs(vp, &lf, 1);
	if (error) {
		flp->error = FIOLOG_ENOULOCK;
		error = 0;
		goto out;
	}
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LQFS_TODO_LOCKFS */

	/*
	 * There's nothing in the log yet (we've just allocated it)
	 * so directly write out the super block.
	 * Note, we have to force this sb out to disk
	 * (not just to the log) so that if we crash we know we are logging
	 */
	VFS_LOCK_MUTEX_ENTER(qfsvfsp);
	LQFS_SET_FS_CLEAN(fs, FSLOG);
	LQFS_SET_FS_ROLLED(fs, FS_NEED_ROLL);	/* Mark the fs as unrolled */
#ifdef LUFS
	QFS_BWRITE2(NULL, qfsvfsp->vfs_bufp);
#else
	sam_update_sblk(qfsvfsp, 0, 0, TRUE);
#endif /* LUFS */
	VFS_LOCK_MUTEX_EXIT(qfsvfsp);

	error = 0;
	goto out;

errout:
	/*
	 * Aquire the qfs_scan_lock before de-linking the mtm data
	 * structure so that we keep qfs_sync() and qfs_update() away
	 * when they execute the ufs_scan_inodes() run while we're in
	 * progress of enabling/disabling logging.
	 */
	mutex_enter(&qfs_scan_lock);
	(void) lqfs_unsnarf(qfsvfsp);
	mutex_exit(&qfs_scan_lock);

	(void) lqfs_free(qfsvfsp);
unlockout:
#ifdef LQFS_TODO_LOCKFS
	mutex_exit(&ulp->ul_lock);
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LQFS_TODO_LOCKFS */

	vfs_unlock(vfsp);

#ifdef LQFS_TODO_LOCKFS
	lf.lf_lock = LOCKFS_ULOCK;
	lf.lf_flags = 0;
	(void) qfs_fiolfs(vp, &lf, 1);
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LQFS_TODO_LOCKFS */

out:
	mutex_enter(&ip->mp->ms.m_waitwr_mutex);
	ip->mp->mt.fi_status |= FS_LOGSTATE_KNOWN;
	mutex_exit(&ip->mp->ms.m_waitwr_mutex);
	return (error);
}
Пример #11
0
/*
 * Disable logging
 */
int
lqfs_disable(vnode_t *vp, struct fiolog *flp)
{
	int		error = 0;
	inode_t		*ip = VTOI(vp);
	qfsvfs_t	*qfsvfsp = ip->i_qfsvfs;
	fs_lqfs_common_t	*fs = VFS_FS_PTR(qfsvfsp);
#ifdef LUFS
	struct lockfs	lf;
	struct ulockfs	*ulp;
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LUFS */

	flp->error = FIOLOG_ENONE;

	/*
	 * Logging is already disabled; done
	 */
	if (LQFS_GET_LOGBNO(fs) == 0 || LQFS_GET_LOGP(qfsvfsp) == NULL ||
	    !LQFS_CAPABLE(qfsvfsp)) {
		vfs_setmntopt(qfsvfsp->vfs_vfs, MNTOPT_NOLOGGING, NULL, 0);
		error = 0;
		goto out;
	}

#ifdef LUFS
	/*
	 * File system must be write locked to disable logging
	 */
	error = qfs_fiolfss(vp, &lf);
	if (error) {
		goto out;
	}
	if (!LOCKFS_IS_ULOCK(&lf)) {
		flp->error = FIOLOG_EULOCK;
		error = 0;
		goto out;
	}
	lf.lf_lock = LOCKFS_WLOCK;
	lf.lf_flags = 0;
	lf.lf_comment = NULL;
	error = qfs_fiolfs(vp, &lf, 1);
	if (error) {
		flp->error = FIOLOG_EWLOCK;
		error = 0;
		goto out;
	}
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LUFS */

	if (LQFS_GET_LOGP(qfsvfsp) == NULL || LQFS_GET_LOGBNO(fs) == 0) {
		goto errout;
	}

	/*
	 * WE ARE COMMITTED TO DISABLING LOGGING PAST THIS POINT
	 */

	/*
	 * Disable logging:
	 * Suspend the reclaim thread and force the delete thread to exit.
	 *	When a nologging mount has completed there may still be
	 *	work for reclaim to do so just suspend this thread until
	 *	it's [deadlock-] safe for it to continue.  The delete
	 *	thread won't be needed as qfs_iinactive() calls
	 *	qfs_delete() when logging is disabled.
	 * Freeze and drain reader ops.
	 *	Commit any outstanding reader transactions (lqfs_flush).
	 *	Set the ``unmounted'' bit in the qfstrans struct.
	 *	If debug, remove metadata from matamap.
	 *	Disable matamap processing.
	 *	NULL the trans ops table.
	 *	Free all of the incore structs related to logging.
	 * Allow reader ops.
	 */
#ifdef LUFS
	qfs_thread_suspend(&qfsvfsp->vfs_reclaim);
	qfs_thread_exit(&qfsvfsp->vfs_delete);
#else
	/* QFS doesn't have file reclaim nor i-node delete threads. */
#endif /* LUFS */

	vfs_lock_wait(qfsvfsp->vfs_vfs);
#ifdef LQFS_TODO_LOCKFS
	ulp = &qfsvfsp->vfs_ulockfs;
	mutex_enter(&ulp->ul_lock);
	(void) qfs_quiesce(ulp);
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LQFS_TODO_LOCKFS */

#ifdef LQFS_TODO
	(void) qfs_flush(qfsvfsp->vfs_vfs);
#else
	(void) lqfs_flush(qfsvfsp);
	if (LQFS_GET_LOGP(qfsvfsp)) {
		logmap_start_roll(LQFS_GET_LOGP(qfsvfsp));
	}
#endif /* LQFS_TODO */

	TRANS_MATA_UMOUNT(qfsvfsp);
	LQFS_SET_DOMATAMAP(qfsvfsp, 0);

	/*
	 * Free all of the incore structs
	 * Aquire the ufs_scan_lock before de-linking the mtm data
	 * structure so that we keep ufs_sync() and ufs_update() away
	 * when they execute the ufs_scan_inodes() run while we're in
	 * progress of enabling/disabling logging.
	 */
	mutex_enter(&qfs_scan_lock);
	(void) lqfs_unsnarf(qfsvfsp);
	mutex_exit(&qfs_scan_lock);

#ifdef LQFS_TODO_LOCKFS
	atomic_add_long(&ufs_quiesce_pend, -1);
	mutex_exit(&ulp->ul_lock);
#else
	/* QFS doesn't do this yet. */
#endif /* LQFS_TODO_LOCKFS */
	vfs_setmntopt(qfsvfsp->vfs_vfs, MNTOPT_NOLOGGING, NULL, 0);
	vfs_unlock(qfsvfsp->vfs_vfs);

	LQFS_SET_FS_ROLLED(fs, FS_ALL_ROLLED);
	LQFS_SET_NOLOG_SI(qfsvfsp, 0);

	/*
	 * Free the log space and mark the superblock as FSACTIVE
	 */
	(void) lqfs_free(qfsvfsp);

#ifdef LUFS
	/*
	 * Allow the reclaim thread to continue.
	 */
	qfs_thread_continue(&qfsvfsp->vfs_reclaim);
#else
	/* QFS doesn't have a file reclaim thread. */
#endif /* LUFS */

#ifdef LQFS_TODO_LOCKFS
	/*
	 * Unlock the file system
	 */
	lf.lf_lock = LOCKFS_ULOCK;
	lf.lf_flags = 0;
	error = qfs_fiolfs(vp, &lf, 1);
	if (error) {
		flp->error = FIOLOG_ENOULOCK;
	}
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LQFS_LOCKFS */

	error = 0;
	goto out;

errout:
#ifdef LQFS_LOCKFS
	lf.lf_lock = LOCKFS_ULOCK;
	lf.lf_flags = 0;
	(void) qfs_fiolfs(vp, &lf, 1);
#else
	/* QFS doesn't really support LOCKFS. */
#endif /* LQFS_LOCKFS */

out:
	mutex_enter(&ip->mp->ms.m_waitwr_mutex);
	ip->mp->mt.fi_status |= FS_LOGSTATE_KNOWN;
	mutex_exit(&ip->mp->ms.m_waitwr_mutex);
	return (error);
}
Пример #12
0
/*
 * Set the quota file up for a particular file system.
 * Called as the result of a quotaon (Q_QUOTAON) ioctl.
 */
static int
opendq(
	struct ufsvfs *ufsvfsp,
	struct vnode *vp,		/* quota file */
	struct cred *cr)
{
	struct inode *qip;
	struct dquot *dqp;
	int error;
	int quotaon = 0;

	if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0)
		return (EPERM);

	VN_HOLD(vp);

	/*
	 * Check to be sure its a regular file.
	 */
	if (vp->v_type != VREG) {
		VN_RELE(vp);
		return (EACCES);
	}

	rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER);

	/*
	 * We have vfs_dqrwlock as writer, so if quotas are disabled,
	 * then vfs_qinod should be NULL or we have a race somewhere.
	 */
	ASSERT((ufsvfsp->vfs_qflags & MQ_ENABLED) || (ufsvfsp->vfs_qinod == 0));

	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) != 0) {
		/*
		 * Quotas are already enabled on this file system.
		 *
		 * If the "quotas" file was replaced (different inode)
		 * while quotas were enabled we don't want to re-enable
		 * them with a new "quotas" file. Simply print a warning
		 * message to the console, release the new vnode, and
		 * return.
		 * XXX - The right way to fix this is to return EBUSY
		 * for the ioctl() issued by 'quotaon'.
		 */
		if (VTOI(vp) != ufsvfsp->vfs_qinod) {
			cmn_err(CE_WARN, "Previous quota file still in use."
			    " Disable quotas on %s before enabling.\n",
			    VTOI(vp)->i_fs->fs_fsmnt);
			VN_RELE(vp);
			rw_exit(&ufsvfsp->vfs_dqrwlock);
			return (0);
		}
		(void) quotasync(ufsvfsp, /* do_lock */ 0);
		/* remove extra hold on quota file */
		VN_RELE(vp);
		quotaon++;
		qip = ufsvfsp->vfs_qinod;
	} else {
		int qlen;

		ufsvfsp->vfs_qinod = VTOI(vp);
		qip = ufsvfsp->vfs_qinod;
		/*
		 * Force the file to have no partially allocated blocks
		 * to prevent a realloc from changing the location of
		 * the data. We must do this even if not logging in
		 * case we later remount to logging.
		 */
		qlen = qip->i_fs->fs_bsize * NDADDR;

		/*
		 * Largefiles: i_size needs to be atomically accessed now.
		 */
		rw_enter(&qip->i_contents, RW_WRITER);
		if (qip->i_size < qlen) {
			if (ufs_itrunc(qip, (u_offset_t)qlen, (int)0, cr) != 0)
				cmn_err(CE_WARN, "opendq failed to remove frags"
				    " from quota file\n");
			rw_exit(&qip->i_contents);
			(void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)qip->i_size,
			    B_INVAL, kcred, NULL);
		} else {
			rw_exit(&qip->i_contents);
		}
		TRANS_MATA_IGET(ufsvfsp, qip);
	}

	/*
	 * The file system time limits are in the dquot for uid 0.
	 * The time limits set the relative time the other users
	 * can be over quota for this file system.
	 * If it is zero a default is used (see quota.h).
	 */
	error = getdiskquota((uid_t)0, ufsvfsp, 1, &dqp);
	if (error == 0) {
		mutex_enter(&dqp->dq_lock);
		ufsvfsp->vfs_btimelimit =
		    (dqp->dq_btimelimit? dqp->dq_btimelimit: DQ_BTIMELIMIT);
		ufsvfsp->vfs_ftimelimit =
		    (dqp->dq_ftimelimit? dqp->dq_ftimelimit: DQ_FTIMELIMIT);

		ufsvfsp->vfs_qflags = MQ_ENABLED;	/* enable quotas */
		vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_QUOTA, NULL, 0);
		dqput(dqp);
		mutex_exit(&dqp->dq_lock);
	} else if (!quotaon) {
		/*
		 * Some sort of I/O error on the quota file, and quotas were
		 * not already on when we got here so clean up.
		 */
		ufsvfsp->vfs_qflags = 0;
		ufsvfsp->vfs_qinod = NULL;
		VN_RELE(ITOV(qip));
	}

	/*
	 * If quotas are enabled update all valid inodes in the
	 * cache with quota information.
	 */
	if (ufsvfsp->vfs_qflags & MQ_ENABLED) {
		(void) ufs_scan_inodes(0, opendq_scan_inode, ufsvfsp, ufsvfsp);
	}

	rw_exit(&ufsvfsp->vfs_dqrwlock);
	return (error);
}
Пример #13
0
int
domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
    char *fspec, int fsflags)
{
	struct mount *mp;
	struct vfsconf *vfsp;
	struct ucred *newcr, *oldcr;
	int error;
	
	/*
	 * Be ultra-paranoid about making sure the type and fspath
	 * variables will fit in our mp buffers, including the
	 * terminating NUL.
	 */
	if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
		return (ENAMETOOLONG);

	vfsp = vfs_byname_kld(fstype, td, &error);
	if (vfsp == NULL)
		return (ENODEV);

	if (vp->v_type != VDIR)
		return (ENOTDIR);
	simple_lock(&vp->v_interlock);
	if ((vp->v_iflag & VI_MOUNT) != 0 ||
	    vp->v_mountedhere != NULL) {
		simple_unlock(&vp->v_interlock);
		return (EBUSY);
	}
	vp->v_iflag |= VI_MOUNT;
	simple_unlock(&vp->v_interlock);

	/*
	 * Allocate and initialize the filesystem.
	 */
	vn_lock(vp, LK_SHARED | LK_RETRY);
	mp = vfs_mount_alloc(vp, vfsp, fspath, td);
	VOP_UNLOCK(vp);

	mp->mnt_optnew = NULL;
	vfs_setmntopt(mp, "from", fspec, 0);
	mp->mnt_optnew = mp->mnt_opt;
	mp->mnt_opt = NULL;

	/*
	 * Set the mount level flags.
	 * crdup() can sleep, so do it before acquiring a mutex.
	 */
	newcr = crdup(kcred);
	MNT_ILOCK(mp);
	if (fsflags & MNT_RDONLY)
		mp->mnt_flag |= MNT_RDONLY;
	mp->mnt_flag &=~ MNT_UPDATEMASK;
	mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS);
	/*
	 * Unprivileged user can trigger mounting a snapshot, but we don't want
	 * him to unmount it, so we switch to privileged credentials.
	 */
	oldcr = mp->mnt_cred;
	mp->mnt_cred = newcr;
	mp->mnt_stat.f_owner = mp->mnt_cred->cr_uid;
	MNT_IUNLOCK(mp);
	crfree(oldcr);
	/*
	 * Mount the filesystem.
	 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
	 * get.  No freeing of cn_pnbuf.
	 */
	error = VFS_MOUNT(mp, td);

	if (!error) {
		if (mp->mnt_opt != NULL)
			vfs_freeopts(mp->mnt_opt);
		mp->mnt_opt = mp->mnt_optnew;
		(void)VFS_STATFS(mp, &mp->mnt_stat, td);
	}
	/*
	 * Prevent external consumers of mount options from reading
	 * mnt_optnew.
	*/
	mp->mnt_optnew = NULL;
	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
	/*
	 * Put the new filesystem on the mount list after root.
	 */
#ifdef FREEBSD_NAMECACHE
	cache_purge(vp);
#endif
	if (!error) {
		vnode_t *mvp;

		simple_lock(&vp->v_interlock);
		vp->v_iflag &= ~VI_MOUNT;
		simple_unlock(&vp->v_interlock);
		vp->v_mountedhere = mp;
		mountlist_append(mp);
		vfs_event_signal(NULL, VQ_MOUNT, 0);
		if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td))
			panic("mount: lost mount");
		mountcheckdirs(vp, mvp);
		vput(mvp);
		VOP_UNLOCK(vp);
		if ((mp->mnt_flag & MNT_RDONLY) == 0)
			vfs_syncer_add_to_worklist(mp);
		vfs_unbusy(mp, td);
		vfs_mountedfrom(mp, fspec);
	} else {
		simple_lock(&vp->v_interlock);
		vp->v_iflag &= ~VI_MOUNT;
		simple_unlock(&vp->v_interlock);
		VOP_UNLOCK(vp);
		vfs_unbusy(mp, td);
		vfs_mount_destroy(mp);
	}
	return (error);
}
Пример #14
0
int
mount_snapshot(kthread_t *td, vnode_t **vpp, const char *fstype, char *fspath,
    char *fspec, int fsflags)
{
	struct vfsconf *vfsp;
	struct mount *mp;
	vnode_t *vp, *mvp;
	struct ucred *cr;
	int error;

	/*
	 * Be ultra-paranoid about making sure the type and fspath
	 * variables will fit in our mp buffers, including the
	 * terminating NUL.
	 */
	if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
		return (ENAMETOOLONG);

	vfsp = vfs_byname_kld(fstype, td, &error);
	if (vfsp == NULL)
		return (ENODEV);

	vp = *vpp;
	if (vp->v_type != VDIR)
		return (ENOTDIR);
	/*
	 * We need vnode lock to protect v_mountedhere and vnode interlock
	 * to protect v_iflag.
	 */
	vn_lock(vp, LK_SHARED | LK_RETRY);
	VI_LOCK(vp);
	if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) {
		VI_UNLOCK(vp);
		VOP_UNLOCK(vp, 0);
		return (EBUSY);
	}
	vp->v_iflag |= VI_MOUNT;
	VI_UNLOCK(vp);
	VOP_UNLOCK(vp, 0);

	/*
	 * Allocate and initialize the filesystem.
	 * We don't want regular user that triggered snapshot mount to be able
	 * to unmount it, so pass credentials of the parent mount.
	 */
	mp = vfs_mount_alloc(vp, vfsp, fspath, vp->v_mount->mnt_cred);

	mp->mnt_optnew = NULL;
	vfs_setmntopt(mp, "from", fspec, 0);
	mp->mnt_optnew = mp->mnt_opt;
	mp->mnt_opt = NULL;

	/*
	 * Set the mount level flags.
	 */
	mp->mnt_flag = fsflags & MNT_UPDATEMASK;
	/*
	 * Snapshots are always read-only.
	 */
	mp->mnt_flag |= MNT_RDONLY;
	/*
	 * We don't want snapshots to allow access to vulnerable setuid
	 * programs, so we turn off setuid when mounting snapshots.
	 */
	mp->mnt_flag |= MNT_NOSUID;
	/*
	 * We don't want snapshots to be visible in regular
	 * mount(8) and df(1) output.
	 */
	mp->mnt_flag |= MNT_IGNORE;
	/*
	 * XXX: This is evil, but we can't mount a snapshot as a regular user.
	 * XXX: Is is safe when snapshot is mounted from within a jail?
	 */
	cr = td->td_ucred;
	td->td_ucred = kcred;
	error = VFS_MOUNT(mp);
	td->td_ucred = cr;

	if (error != 0) {
		VI_LOCK(vp);
		vp->v_iflag &= ~VI_MOUNT;
		VI_UNLOCK(vp);
		vrele(vp);
		vfs_unbusy(mp);
		vfs_mount_destroy(mp);
		*vpp = NULL;
		return (error);
	}

	if (mp->mnt_opt != NULL)
		vfs_freeopts(mp->mnt_opt);
	mp->mnt_opt = mp->mnt_optnew;
	(void)VFS_STATFS(mp, &mp->mnt_stat);

	/*
	 * Prevent external consumers of mount options from reading
	 * mnt_optnew.
	*/
	mp->mnt_optnew = NULL;

	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
#ifdef FREEBSD_NAMECACHE
	cache_purge(vp);
#endif
	VI_LOCK(vp);
	vp->v_iflag &= ~VI_MOUNT;
	VI_UNLOCK(vp);

	vp->v_mountedhere = mp;
	/* Put the new filesystem on the mount list. */
	mtx_lock(&mountlist_mtx);
	TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
	mtx_unlock(&mountlist_mtx);
	vfs_event_signal(NULL, VQ_MOUNT, 0);
	if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp))
		panic("mount: lost mount");
	vput(vp);
	vfs_unbusy(mp);
	*vpp = mvp;
	return (0);
}
Пример #15
0
/*
 * smbfs mount vfsop
 * Set up mount info record and attach it to vfs struct.
 */
static int
smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
	char		*data = uap->dataptr;
	int		error;
	smbnode_t 	*rtnp = NULL;	/* root of this fs */
	smbmntinfo_t 	*smi = NULL;
	dev_t 		smbfs_dev;
	int 		version;
	int 		devfd;
	zone_t		*zone = curproc->p_zone;
	zone_t		*mntzone = NULL;
	smb_share_t 	*ssp = NULL;
	smb_cred_t 	scred;
	int		flags, sec;

	STRUCT_DECL(smbfs_args, args);		/* smbfs mount arguments */

	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
		return (error);

	if (mvp->v_type != VDIR)
		return (ENOTDIR);

	/*
	 * get arguments
	 *
	 * uap->datalen might be different from sizeof (args)
	 * in a compatible situation.
	 */
	STRUCT_INIT(args, get_udatamodel());
	bzero(STRUCT_BUF(args), SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE));
	if (copyin(data, STRUCT_BUF(args), MIN(uap->datalen,
	    SIZEOF_STRUCT(smbfs_args, DATAMODEL_NATIVE))))
		return (EFAULT);

	/*
	 * Check mount program version
	 */
	version = STRUCT_FGET(args, version);
	if (version != SMBFS_VERSION) {
		cmn_err(CE_WARN, "mount version mismatch:"
		    " kernel=%d, mount=%d\n",
		    SMBFS_VERSION, version);
		return (EINVAL);
	}

	/*
	 * Deal with re-mount requests.
	 */
	if (uap->flags & MS_REMOUNT) {
		cmn_err(CE_WARN, "MS_REMOUNT not implemented");
		return (ENOTSUP);
	}

	/*
	 * Check for busy
	 */
	mutex_enter(&mvp->v_lock);
	if (!(uap->flags & MS_OVERLAY) &&
	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
		mutex_exit(&mvp->v_lock);
		return (EBUSY);
	}
	mutex_exit(&mvp->v_lock);

	/*
	 * Get the "share" from the netsmb driver (ssp).
	 * It is returned with a "ref" (hold) for us.
	 * Release this hold: at errout below, or in
	 * smbfs_freevfs().
	 */
	devfd = STRUCT_FGET(args, devfd);
	error = smb_dev2share(devfd, &ssp);
	if (error) {
		cmn_err(CE_WARN, "invalid device handle %d (%d)\n",
		    devfd, error);
		return (error);
	}

	/*
	 * Use "goto errout" from here on.
	 * See: ssp, smi, rtnp, mntzone
	 */

	/*
	 * Determine the zone we're being mounted into.
	 */
	zone_hold(mntzone = zone);		/* start with this assumption */
	if (getzoneid() == GLOBAL_ZONEID) {
		zone_rele(mntzone);
		mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
		ASSERT(mntzone != NULL);
		if (mntzone != zone) {
			error = EBUSY;
			goto errout;
		}
	}

	/*
	 * Stop the mount from going any further if the zone is going away.
	 */
	if (zone_status_get(mntzone) >= ZONE_IS_SHUTTING_DOWN) {
		error = EBUSY;
		goto errout;
	}

	/*
	 * On a Trusted Extensions client, we may have to force read-only
	 * for read-down mounts.
	 */
	if (is_system_labeled()) {
		void *addr;
		int ipvers = 0;
		struct smb_vc *vcp;

		vcp = SSTOVC(ssp);
		addr = smb_vc_getipaddr(vcp, &ipvers);
		error = smbfs_mount_label_policy(vfsp, addr, ipvers, cr);

		if (error > 0)
			goto errout;

		if (error == -1) {
			/* change mount to read-only to prevent write-down */
			vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
		}
	}

	/* Prevent unload. */
	atomic_inc_32(&smbfs_mountcount);

	/*
	 * Create a mount record and link it to the vfs struct.
	 * No more possiblities for errors from here on.
	 * Tear-down of this stuff is in smbfs_free_smi()
	 *
	 * Compare with NFS: nfsrootvp()
	 */
	smi = kmem_zalloc(sizeof (*smi), KM_SLEEP);

	mutex_init(&smi->smi_lock, NULL, MUTEX_DEFAULT, NULL);
	cv_init(&smi->smi_statvfs_cv, NULL, CV_DEFAULT, NULL);

	rw_init(&smi->smi_hash_lk, NULL, RW_DEFAULT, NULL);
	smbfs_init_hash_avl(&smi->smi_hash_avl);

	smi->smi_share = ssp;
	ssp = NULL;

	/*
	 * Convert the anonymous zone hold acquired via zone_hold() above
	 * into a zone reference.
	 */
	zone_init_ref(&smi->smi_zone_ref);
	zone_hold_ref(mntzone, &smi->smi_zone_ref, ZONE_REF_SMBFS);
	zone_rele(mntzone);
	mntzone = NULL;

	/*
	 * Initialize option defaults
	 */
	smi->smi_flags	= SMI_LLOCK;
	smi->smi_acregmin = SEC2HR(SMBFS_ACREGMIN);
	smi->smi_acregmax = SEC2HR(SMBFS_ACREGMAX);
	smi->smi_acdirmin = SEC2HR(SMBFS_ACDIRMIN);
	smi->smi_acdirmax = SEC2HR(SMBFS_ACDIRMAX);

	/*
	 * All "generic" mount options have already been
	 * handled in vfs.c:domount() - see mntopts stuff.
	 * Query generic options using vfs_optionisset().
	 */
	if (vfs_optionisset(vfsp, MNTOPT_INTR, NULL))
		smi->smi_flags |= SMI_INT;
	if (vfs_optionisset(vfsp, MNTOPT_ACL, NULL))
		smi->smi_flags |= SMI_ACL;

	/*
	 * Get the mount options that come in as smbfs_args,
	 * starting with args.flags (SMBFS_MF_xxx)
	 */
	flags = STRUCT_FGET(args, flags);
	smi->smi_uid 	= STRUCT_FGET(args, uid);
	smi->smi_gid 	= STRUCT_FGET(args, gid);
	smi->smi_fmode	= STRUCT_FGET(args, file_mode) & 0777;
	smi->smi_dmode	= STRUCT_FGET(args, dir_mode) & 0777;

	/*
	 * Hande the SMBFS_MF_xxx flags.
	 */
	if (flags & SMBFS_MF_NOAC)
		smi->smi_flags |= SMI_NOAC;
	if (flags & SMBFS_MF_ACREGMIN) {
		sec = STRUCT_FGET(args, acregmin);
		if (sec < 0 || sec > SMBFS_ACMINMAX)
			sec = SMBFS_ACMINMAX;
		smi->smi_acregmin = SEC2HR(sec);
	}
	if (flags & SMBFS_MF_ACREGMAX) {
		sec = STRUCT_FGET(args, acregmax);
		if (sec < 0 || sec > SMBFS_ACMAXMAX)
			sec = SMBFS_ACMAXMAX;
		smi->smi_acregmax = SEC2HR(sec);
	}
	if (flags & SMBFS_MF_ACDIRMIN) {
		sec = STRUCT_FGET(args, acdirmin);
		if (sec < 0 || sec > SMBFS_ACMINMAX)
			sec = SMBFS_ACMINMAX;
		smi->smi_acdirmin = SEC2HR(sec);
	}
	if (flags & SMBFS_MF_ACDIRMAX) {
		sec = STRUCT_FGET(args, acdirmax);
		if (sec < 0 || sec > SMBFS_ACMAXMAX)
			sec = SMBFS_ACMAXMAX;
		smi->smi_acdirmax = SEC2HR(sec);
	}

	/*
	 * Get attributes of the remote file system,
	 * i.e. ACL support, named streams, etc.
	 */
	smb_credinit(&scred, cr);
	error = smbfs_smb_qfsattr(smi->smi_share, &smi->smi_fsa, &scred);
	smb_credrele(&scred);
	if (error) {
		SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error);
	}

	/*
	 * We enable XATTR by default (via smbfs_mntopts)
	 * but if the share does not support named streams,
	 * force the NOXATTR option (also clears XATTR).
	 * Caller will set or clear VFS_XATTR after this.
	 */
	if ((smi->smi_fsattr & FILE_NAMED_STREAMS) == 0)
		vfs_setmntopt(vfsp, MNTOPT_NOXATTR, NULL, 0);

	/*
	 * Ditto ACLs (disable if not supported on this share)
	 */
	if ((smi->smi_fsattr & FILE_PERSISTENT_ACLS) == 0) {
		vfs_setmntopt(vfsp, MNTOPT_NOACL, NULL, 0);
		smi->smi_flags &= ~SMI_ACL;
	}

	/*
	 * Assign a unique device id to the mount
	 */
	mutex_enter(&smbfs_minor_lock);
	do {
		smbfs_minor = (smbfs_minor + 1) & MAXMIN32;
		smbfs_dev = makedevice(smbfs_major, smbfs_minor);
	} while (vfs_devismounted(smbfs_dev));
	mutex_exit(&smbfs_minor_lock);

	vfsp->vfs_dev	= smbfs_dev;
	vfs_make_fsid(&vfsp->vfs_fsid, smbfs_dev, smbfsfstyp);
	vfsp->vfs_data	= (caddr_t)smi;
	vfsp->vfs_fstype = smbfsfstyp;
	vfsp->vfs_bsize = MAXBSIZE;
	vfsp->vfs_bcount = 0;

	smi->smi_vfsp	= vfsp;
	smbfs_zonelist_add(smi);	/* undo in smbfs_freevfs */

	/*
	 * Create the root vnode, which we need in unmount
	 * for the call to smbfs_check_table(), etc.
	 * Release this hold in smbfs_unmount.
	 */
	rtnp = smbfs_node_findcreate(smi, "\\", 1, NULL, 0, 0,
	    &smbfs_fattr0);
	ASSERT(rtnp != NULL);
	rtnp->r_vnode->v_type = VDIR;
	rtnp->r_vnode->v_flag |= VROOT;
	smi->smi_root = rtnp;

	/*
	 * NFS does other stuff here too:
	 *   async worker threads
	 *   init kstats
	 *
	 * End of code from NFS nfsrootvp()
	 */
	return (0);

errout:
	vfsp->vfs_data = NULL;
	if (smi != NULL)
		smbfs_free_smi(smi);

	if (mntzone != NULL)
		zone_rele(mntzone);

	if (ssp != NULL)
		smb_share_rele(ssp);

	return (error);
}