Example #1
0
/*ARGSUSED3*/
static int
devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
    caller_context_t *ct, int *direntflags, pathname_t *realpnp)
{
	struct sdev_node *sdvp = VTOSDEV(dvp);
	struct sdev_node *dv;
	struct vnode *rvp = NULL;
	int error;

	error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp,
	    SDEV_VATTR);

	if (error == 0) {
		switch ((*vpp)->v_type) {
		case VCHR:
			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
			ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
			break;
		case VDIR:
			dv = VTOSDEV(*vpp);
			break;
		default:
			cmn_err(CE_PANIC, "devpts_lookup: Unsupported node "
			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
			break;
		}
		ASSERT(SDEV_HELD(dv));
	}

	return (error);
}
Example #2
0
/*
 * Return the vnode representing the file descriptor in vpp.
 */
static int
nm_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
{
	struct vnode *rvp;

	vp = VTONM(vp)->nm_filevp;
	if (VOP_REALVP(vp, &rvp, ct) == 0)
		vp = rvp;
	*vpp = vp;
	return (0);
}
Example #3
0
/*
 * See if vp is in use by the accounting system on any zone.  This does a deep
 * comparison of vnodes such that a file and a lofs "shadow" node of it will
 * appear to be the same.
 *
 * If 'compare_vfs' is true, the function will do a comparison of vfs_t's
 * instead (ie, is the vfs_t on which the vnode resides in use by the
 * accounting system in any zone).
 *
 * Returns 1 if found (in use), 0 otherwise.
 */
static int
acct_find(vnode_t *vp, boolean_t compare_vfs)
{
	struct acct_globals *ag;
	vnode_t *realvp;

	ASSERT(MUTEX_HELD(&acct_list_lock));
	ASSERT(vp != NULL);

	if (VOP_REALVP(vp, &realvp, NULL))
		realvp = vp;
	for (ag = list_head(&acct_list); ag != NULL;
	    ag = list_next(&acct_list, ag)) {
		vnode_t *racctvp;
		boolean_t found = B_FALSE;

		mutex_enter(&ag->aclock);
		if (ag->acctvp == NULL) {
			mutex_exit(&ag->aclock);
			continue;
		}
		if (VOP_REALVP(ag->acctvp, &racctvp, NULL))
			racctvp = ag->acctvp;
		if (compare_vfs) {
			if (racctvp->v_vfsp == realvp->v_vfsp)
				found = B_TRUE;
		} else {
			if (VN_CMP(realvp, racctvp))
				found = B_TRUE;
		}
		mutex_exit(&ag->aclock);
		if (found)
			return (1);
	}
	return (0);
}
Example #4
0
/*ARGSUSED3*/
static int
devvt_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
    caller_context_t *ct, int *direntflags, pathname_t *realpnp)
{
	struct sdev_node *sdvp = VTOSDEV(dvp);
	struct sdev_node *dv;
	struct vnode *rvp = NULL;
	int type, error;

	if ((strcmp(nm, DEVVT_ACTIVE_NAME) == 0) ||
	    (strcmp(nm, DEVVT_CONSUSER_NAME) == 0)) {
		type = SDEV_VLINK;
	} else {
		type = SDEV_VATTR;
	}

/* Give warlock a more clear call graph */
#ifndef __lock_lint
	error = devname_lookup_func(sdvp, nm, vpp, cred,
	    devvt_create_rvp, type);
#else
	devvt_create_rvp(0, 0, 0, 0, 0, 0);
#endif

	if (error == 0) {
		switch ((*vpp)->v_type) {
		case VCHR:
			dv = VTOSDEV(VTOS(*vpp)->s_realvp);
			ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
			break;
		case VDIR:
		case VLNK:
			dv = VTOSDEV(*vpp);
			break;
		default:
			cmn_err(CE_PANIC, "devvt_lookup: Unsupported node "
			    "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
			break;
		}
		ASSERT(SDEV_HELD(dv));
	}

	return (error);
}
Example #5
0
static void
prof_getattr(struct sdev_node *dir, char *name, struct vnode *gdv,
    struct vattr *vap, struct vnode **avpp, int *no_fs_perm)
{
	struct vnode *advp;

	/* get attribute from shadow, if present; else get default */
	advp = dir->sdev_attrvp;
	if (advp && VOP_LOOKUP(advp, name, avpp, NULL, 0, NULL, kcred,
	    NULL, NULL, NULL) == 0) {
		(void) VOP_GETATTR(*avpp, vap, 0, kcred, NULL);
	} else if (gdv == NULL || gdv->v_type == VDIR) {
		/* always create shadow directory */
		*vap = sdev_vattr_dir;
		if (advp && VOP_MKDIR(advp, name, &sdev_vattr_dir,
		    avpp, kcred, NULL, 0, NULL) != 0) {
			*avpp = NULLVP;
			sdcmn_err10(("prof_getattr: failed to create "
			    "shadow directory %s/%s\n", dir->sdev_path, name));
		}
	} else {
		/*
		 * get default permission from devfs
		 * Before calling devfs_get_defattr, we need to get
		 * the realvp (the dv_node). If realvp is not a dv_node,
		 * devfs_get_defattr() will return a system-wide default
		 * attr for device nodes.
		 */
		struct vnode *rvp;
		if (VOP_REALVP(gdv, &rvp, NULL) != 0)
			rvp = gdv;
		devfs_get_defattr(rvp, vap, no_fs_perm);
		*avpp = NULLVP;
	}

	/* ignore dev_t and vtype from backing store */
	if (gdv) {
		vap->va_type = gdv->v_type;
		vap->va_rdev = gdv->v_rdev;
	}
}
Example #6
0
static int
xmem_link(struct vnode *dvp, struct vnode *srcvp, char *tnm, struct cred *cred)
{
	struct xmemnode *parent;
	struct xmemnode *from;
	struct xmount *xm = (struct xmount *)VTOXM(dvp);
	int error;
	struct xmemnode *found = NULL;
	struct vnode *realvp;

	if (VOP_REALVP(srcvp, &realvp) == 0)
		srcvp = realvp;

	parent = (struct xmemnode *)VTOXN(dvp);
	from = (struct xmemnode *)VTOXN(srcvp);

	if ((srcvp->v_type == VDIR &&
	    secpolicy_fs_linkdir(cred, dvp->v_vfsp) != 0) ||
	    (from->xn_uid != crgetuid(cred) && secpolicy_basic_link(cred) != 0))
		return (EPERM);

	error = xdirlookup(parent, tnm, &found, cred);
	if (error == 0) {
		ASSERT(found);
		xmemnode_rele(found);
		return (EEXIST);
	}

	if (error != ENOENT)
		return (error);

	rw_enter(&parent->xn_rwlock, RW_WRITER);
	error = xdirenter(xm, parent, tnm, DE_LINK, (struct xmemnode *)NULL,
		from, NULL, (struct xmemnode **)NULL, cred);
	rw_exit(&parent->xn_rwlock);
	return (error);
}
Example #7
0
static int
xmem_rename(
	struct vnode *odvp,	/* source parent vnode */
	char *onm,		/* source name */
	struct vnode *ndvp,	/* destination parent vnode */
	char *nnm,		/* destination name */
	struct cred *cred)
{
	struct xmemnode *fromparent;
	struct xmemnode *toparent;
	struct xmemnode *fromxp = NULL;	/* source xmemnode */
	struct xmount *xm = (struct xmount *)VTOXM(odvp);
	int error;
	int samedir = 0;	/* set if odvp == ndvp */
	struct vnode *realvp;

	if (VOP_REALVP(ndvp, &realvp) == 0)
		ndvp = realvp;

	fromparent = (struct xmemnode *)VTOXN(odvp);
	toparent = (struct xmemnode *)VTOXN(ndvp);

	mutex_enter(&xm->xm_renamelck);

	/*
	 * Look up xmemnode of file we're supposed to rename.
	 */
	error = xdirlookup(fromparent, onm, &fromxp, cred);
	if (error) {
		mutex_exit(&xm->xm_renamelck);
		return (error);
	}

	/*
	 * Make sure we can delete the old (source) entry.  This
	 * requires write permission on the containing directory.  If
	 * that directory is "sticky" it further requires (except for
	 * for privileged users) that the user own the directory or
	 * the source entry, or else have permission to write the
	 * source entry.
	 */
	if (((error = xmem_xaccess(fromparent, VWRITE, cred)) != 0) ||
	    (error = xmem_sticky_remove_access(fromparent, fromxp, cred)) != 0)
		goto done;

	/*
	 * Check for renaming to or from '.' or '..' or that
	 * fromxp == fromparent
	 */
	if ((onm[0] == '.' &&
	    (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
	    (nnm[0] == '.' &&
	    (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0'))) ||
	    (fromparent == fromxp)) {
		error = EINVAL;
		goto done;
	}

	samedir = (fromparent == toparent);
	/*
	 * Make sure we can search and rename into the new
	 * (destination) directory.
	 */
	if (!samedir) {
		error = xmem_xaccess(toparent, VEXEC|VWRITE, cred);
		if (error)
			goto done;
	}

	/*
	 * Link source to new target
	 */
	rw_enter(&toparent->xn_rwlock, RW_WRITER);
	error = xdirenter(xm, toparent, nnm, DE_RENAME,
	    fromparent, fromxp, (struct vattr *)NULL,
	    (struct xmemnode **)NULL, cred);
	rw_exit(&toparent->xn_rwlock);

	if (error) {
		/*
		 * ESAME isn't really an error; it indicates that the
		 * operation should not be done because the source and target
		 * are the same file, but that no error should be reported.
		 */
		if (error == ESAME)
			error = 0;
		goto done;
	}

	/*
	 * Unlink from source.
	 */
	rw_enter(&fromparent->xn_rwlock, RW_WRITER);
	rw_enter(&fromxp->xn_rwlock, RW_WRITER);

	error = xdirdelete(fromparent, fromxp, onm, DR_RENAME, cred);

	/*
	 * The following handles the case where our source xmemnode was
	 * removed before we got to it.
	 *
	 * XXX We should also cleanup properly in the case where xdirdelete
	 * fails for some other reason.  Currently this case shouldn't happen.
	 * (see 1184991).
	 */
	if (error == ENOENT)
		error = 0;

	rw_exit(&fromxp->xn_rwlock);
	rw_exit(&fromparent->xn_rwlock);
done:
	xmemnode_rele(fromxp);
	mutex_exit(&xm->xm_renamelck);

	return (error);
}
Example #8
0
extern int
vnode_iop_link(
    DENT_T * olddent,
    INODE_T * parent,
    DENT_T * newdent
)
{
    int err = 0;
    struct link_ctx ctx;
    VATTR_T *vap;
    VNODE_T *parentvp;

    ASSERT_I_SEM_MINE(olddent->d_inode);
    ASSERT_I_SEM_MINE(parent);
    ASSERT(MDKI_INOISMVFS(parent));

    if (!vnlayer_link_eligible(olddent))
        return -EXDEV;

    /* VOP_REALVP will check that the parent is a loopback directory and
     * return EINVAL if it isn't.
     */
    if (VOP_REALVP(ITOV(parent), &parentvp) == 0) {
        /* We are creating a shadow link so bypass the mvfs for the rest */
        err = vnlayer_do_linux_link(parentvp, olddent, parent, newdent);
        err = mdki_errno_unix_to_linux(err);
    } else {
        /* This needs to be passed on to the mvfs to deal with */
        CALL_DATA_T cd;
        INODE_T *inode;
        if (!MDKI_INOISOURS(olddent->d_inode))
            return -EXDEV;
        ctx.parent = parent;
        ctx.newdent = newdent;
        ctx.olddent = olddent;
        ctx.done = FALSE;

        mdki_linux_init_call_data(&cd);
	if (MDKI_INOISMVFS(olddent->d_inode)) {
            err = VOP_LINK(ITOV(parent), ITOV(olddent->d_inode),
                           (char *)newdent->d_name.name, &cd, &ctx);
            err = mdki_errno_unix_to_linux(err);
            if (err == 0 && !ctx.done) {
                /* Again, a heavy handed way of bumping the inode count and
                 * handling the locking (This will use the inode lock)
                 */
                inode = igrab(olddent->d_inode);
                VNODE_D_INSTANTIATE(newdent, inode);
                if ((vap = VATTR_ALLOC()) != NULL) {
                    VATTR_SET_MASK(vap, AT_ALL);
                    if (VOP_GETATTR(ITOV(inode), vap, 0, &cd) == 0)
                        mdki_linux_vattr_pullup(ITOV(inode), vap, AT_ALL);
                    VATTR_FREE(vap);
		}
            }
	} else {
	    err = -EXDEV;
	}
        mdki_linux_destroy_call_data(&cd);
    }
    return err;
}
Example #9
0
/*
 * The additional flag, LOOKUP_CHECKREAD, is used to enforce artificial
 * constraints in order to be standards compliant.  For example, if we have
 * the cached path of '/foo/bar', and '/foo' has permissions 100 (execute
 * only), then we can legitimately look up the path to the current working
 * directory without needing read permission.  Existing standards tests,
 * however, assume that we are determining the path by repeatedly looking up
 * "..".  We need to keep this behavior in order to maintain backwards
 * compatibility.
 */
static int
vnodetopath_common(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen,
    cred_t *cr, int flags)
{
	pathname_t pn, rpn;
	int ret, len;
	vnode_t *compvp, *pvp, *realvp;
	proc_t *p = curproc;
	char path[MAXNAMELEN];
	int doclose = 0;

	/*
	 * If vrootp is NULL, get the root for curproc.  Callers with any other
	 * requirements should pass in a different vrootp.
	 */
	if (vrootp == NULL) {
		mutex_enter(&p->p_lock);
		if ((vrootp = PTOU(p)->u_rdir) == NULL)
			vrootp = rootdir;
		VN_HOLD(vrootp);
		mutex_exit(&p->p_lock);
	} else {
		VN_HOLD(vrootp);
	}

	/*
	 * This is to get around an annoying artifact of the /proc filesystem,
	 * which is the behavior of {cwd/root}.  Trying to resolve this path
	 * will result in /proc/pid/cwd instead of whatever the real working
	 * directory is.  We can't rely on VOP_REALVP(), since that will break
	 * lofs.  The only difference between procfs and lofs is that opening
	 * the file will return the underling vnode in the case of procfs.
	 */
	if (vp->v_type == VDIR && VOP_REALVP(vp, &realvp, NULL) == 0 &&
	    realvp != vp) {
		VN_HOLD(vp);
		if (VOP_OPEN(&vp, FREAD, cr, NULL) == 0)
			doclose = 1;
		else
			VN_RELE(vp);
	}

	pn_alloc(&pn);

	/*
	 * Check to see if we have a cached path in the vnode.
	 */
	mutex_enter(&vp->v_lock);
	if (vp->v_path != NULL) {
		(void) pn_set(&pn, vp->v_path);
		mutex_exit(&vp->v_lock);

		pn_alloc(&rpn);

		/* We should only cache absolute paths */
		ASSERT(pn.pn_buf[0] == '/');

		/*
		 * If we are in a zone or a chroot environment, then we have to
		 * take additional steps, since the path to the root might not
		 * be readable with the current credentials, even though the
		 * process can legitmately access the file.  In this case, we
		 * do the following:
		 *
		 * lookuppnvp() with all privileges to get the resolved path.
		 * call localpath() to get the local portion of the path, and
		 * continue as normal.
		 *
		 * If the the conversion to a local path fails, then we continue
		 * as normal.  This is a heuristic to make process object file
		 * paths available from within a zone.  Because lofs doesn't
		 * support page operations, the vnode stored in the seg_t is
		 * actually the underlying real vnode, not the lofs node itself.
		 * Most of the time, the lofs path is the same as the underlying
		 * vnode (for example, /usr/lib/libc.so.1).
		 */
		if (vrootp != rootdir) {
			char *local = NULL;
			VN_HOLD(rootdir);
			if (lookuppnvp(&pn, &rpn, FOLLOW,
			    NULL, &compvp, rootdir, rootdir, kcred) == 0) {
				local = localpath(rpn.pn_path, vrootp,
				    kcred);
				VN_RELE(compvp);
			}

			/*
			 * The original pn was changed through lookuppnvp().
			 * Set it to local for next validation attempt.
			 */
			if (local) {
				(void) pn_set(&pn, local);
			} else {
				goto notcached;
			}
		}

		/*
		 * We should have a local path at this point, so start the
		 * search from the root of the current process.
		 */
		VN_HOLD(vrootp);
		if (vrootp != rootdir)
			VN_HOLD(vrootp);
		ret = lookuppnvp(&pn, &rpn, FOLLOW | flags, NULL,
		    &compvp, vrootp, vrootp, cr);
		if (ret == 0) {
			/*
			 * Check to see if the returned vnode is the same as
			 * the one we expect.  If not, give up.
			 */
			if (!vn_compare(vp, compvp) &&
			    !vnode_match(vp, compvp, cr)) {
				VN_RELE(compvp);
				goto notcached;
			}

			VN_RELE(compvp);

			/*
			 * Return the result.
			 */
			if (buflen <= rpn.pn_pathlen)
				goto notcached;

			bcopy(rpn.pn_path, buf, rpn.pn_pathlen + 1);
			pn_free(&pn);
			pn_free(&rpn);
			VN_RELE(vrootp);
			if (doclose) {
				(void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL);
				VN_RELE(vp);
			}
			return (0);
		}

notcached:
		pn_free(&rpn);
	} else {
		mutex_exit(&vp->v_lock);
	}

	pn_free(&pn);

	if (vp->v_type != VDIR) {
		/*
		 * If we don't have a directory, try to find it in the dnlc via
		 * reverse lookup.  Once this is found, we can use the regular
		 * directory search to find the full path.
		 */
		if ((pvp = dnlc_reverse_lookup(vp, path, MAXNAMELEN)) != NULL) {
			/*
			 * Check if we have read privilege so, that
			 * we can lookup the path in the directory
			 */
			ret = 0;
			if ((flags & LOOKUP_CHECKREAD)) {
				ret = VOP_ACCESS(pvp, VREAD, 0, cr, NULL);
			}
			if (ret == 0) {
				ret = dirtopath(vrootp, pvp, buf, buflen,
				    flags, cr);
			}
			if (ret == 0) {
				len = strlen(buf);
				if (len + strlen(path) + 1 >= buflen) {
					ret = ENAMETOOLONG;
				} else {
					if (buf[len - 1] != '/')
						buf[len++] = '/';
					bcopy(path, buf + len,
					    strlen(path) + 1);
				}
			}

			VN_RELE(pvp);
		} else
			ret = ENOENT;
	} else
		ret = dirtopath(vrootp, vp, buf, buflen, flags, cr);

	VN_RELE(vrootp);
	if (doclose) {
		(void) VOP_CLOSE(vp, FREAD, 1, 0, cr, NULL);
		VN_RELE(vp);
	}

	return (ret);
}
Example #10
0
/*
 * Provide a shadow for a vnode.  We create a new shadow before checking for an
 * existing one, to minimize the amount of time we need to hold ftable_lock.
 * If a vp already has a shadow in the hash list, return its shadow.  If not,
 * we hash the new vnode and return its pointer to the caller.
 */
vnode_t *
fifovp(vnode_t *vp, cred_t *crp)
{
	fifonode_t *fnp;
	fifonode_t *spec_fnp;   /* Speculative fnode ptr. */
	fifodata_t *fdp;
	vnode_t *newvp;
	struct vattr va;
	vnode_t	*rvp;

	ASSERT(vp != NULL);

	fdp = kmem_cache_alloc(fnode_cache, KM_SLEEP);

	fdp->fifo_lock.flk_ref = 1;
	fnp = &fdp->fifo_fnode[0];

	/*
	 * Its possible that fifo nodes on different lofs mountpoints
	 * shadow the same real filesystem fifo node.
	 * In this case its necessary to get and store the realvp.
	 * This way different fifo nodes sharing the same real vnode
	 * can use realvp for communication.
	 */

	if (VOP_REALVP(vp, &rvp, NULL) == 0)
			vp = rvp;

	fnp->fn_realvp	= vp;
	fnp->fn_wcnt	= 0;
	fnp->fn_rcnt	= 0;

#if FIFODEBUG
	if (! Fifo_fastmode) {
		fnp->fn_flag	= 0;
	} else {
		fnp->fn_flag	= FIFOFAST;
	}
#else /* FIFODEBUG */
	fnp->fn_flag	= FIFOFAST;
#endif /* FIFODEBUG */

	/*
	 * initialize the times from vp.
	 */
	va.va_mask = AT_TIMES;
	if (VOP_GETATTR(vp, &va, 0, crp, NULL) == 0) {
		fnp->fn_atime = va.va_atime.tv_sec;
		fnp->fn_mtime = va.va_mtime.tv_sec;
		fnp->fn_ctime = va.va_ctime.tv_sec;
	} else {
		fnp->fn_atime = 0;
		fnp->fn_mtime = 0;
		fnp->fn_ctime = 0;
	}

	/*
	 * Grab the VP here to avoid holding locks
	 * whilst trying to acquire others.
	 */

	VN_HOLD(vp);

	mutex_enter(&ftable_lock);

	if ((spec_fnp = fifofind(vp)) != NULL) {
		mutex_exit(&ftable_lock);

		/*
		 * Release the vnode and free up our pre-prepared fnode.
		 * Zero the lock reference just to explicitly signal
		 * this is unused.
		 */
		VN_RELE(vp);
		fdp->fifo_lock.flk_ref = 0;
		kmem_cache_free(fnode_cache, fdp);

		return (FTOV(spec_fnp));
	}

	newvp = FTOV(fnp);
	fifo_reinit_vp(newvp);
	/*
	 * Since the fifo vnode's v_vfsp needs to point to the
	 * underlying filesystem's vfsp we need to bump up the
	 * underlying filesystem's vfs reference count.
	 * The count is decremented when the fifo node is
	 * inactivated.
	 */

	VFS_HOLD(vp->v_vfsp);
	newvp->v_vfsp = vp->v_vfsp;
	newvp->v_rdev = vp->v_rdev;
	newvp->v_flag |= (vp->v_flag & VROOT);

	fifoinsert(fnp);
	mutex_exit(&ftable_lock);

	return (newvp);
}
Example #11
0
/*
 * getflabel -
 *
 * Return pointer to the ts_label associated with the specified file,
 * or returns NULL if error occurs.  Caller is responsible for doing
 * a label_rele of the ts_label.
 */
ts_label_t *
getflabel(vnode_t *vp)
{
	vfs_t		*vfsp, *rvfsp;
	vnode_t		*rvp, *rvp2;
	zone_t		*zone;
	ts_label_t	*zl;
	int		err;
	boolean_t	vfs_is_held = B_FALSE;
	char		vpath[MAXPATHLEN];

	ASSERT(vp);
	vfsp = vp->v_vfsp;
	if (vfsp == NULL)
		return (NULL);

	rvp = vp;

	/*
	 * Traverse lofs mounts and fattach'es to get the real vnode
	 */
	if (VOP_REALVP(rvp, &rvp2, NULL) == 0)
		rvp = rvp2;

	rvfsp = rvp->v_vfsp;

	/* rvp/rvfsp now represent the real vnode/vfs we will be using */

	/* Go elsewhere to handle all nfs files. */
	if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "nfs", 3) == 0)
		return (getflabel_nfs(rvfsp));

	/*
	 * Fast path, for objects in a labeled zone: everything except
	 * for lofs/nfs will be just the label of that zone.
	 */
	if ((rvfsp->vfs_zone != NULL) && (rvfsp->vfs_zone != global_zone)) {
		if ((strcmp(vfssw[rvfsp->vfs_fstype].vsw_name,
		    "lofs") != 0)) {
			zone = rvfsp->vfs_zone;
			zone_hold(zone);
			goto zone_out;		/* return this label */
		}
	}

	/*
	 * Get the vnode path -- it may be missing or weird for some
	 * cases, like devices.  In those cases use the label of the
	 * current zone.
	 */
	err = vnodetopath(rootdir, rvp, vpath, sizeof (vpath), kcred);
	if ((err != 0) || (*vpath != '/')) {
		zone = curproc->p_zone;
		zone_hold(zone);
		goto zone_out;
	}

	/*
	 * For zfs filesystem, return the explicit label property if a
	 * meaningful one exists.
	 */
	if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "zfs", 3) == 0) {
		ts_label_t *tsl;

		tsl = getflabel_zfs(rvfsp);

		/* if label found, return it, otherwise continue... */
		if (tsl != NULL)
			return (tsl);
	}

	/*
	 * If a mountpoint exists, hold the vfs while we reference it.
	 * Otherwise if mountpoint is NULL it should not be held (e.g.,
	 * a hold/release on spec_vfs would result in an attempted free
	 * and panic.)
	 */
	if (vfsp->vfs_mntpt != NULL) {
		VFS_HOLD(vfsp);
		vfs_is_held = B_TRUE;
	}

	zone = zone_find_by_any_path(vpath, B_FALSE);

	/*
	 * If the vnode source zone is properly set to a non-global zone, or
	 * any zone if the mount is R/W, then use the label of that zone.
	 */
	if ((zone != global_zone) || ((vfsp->vfs_flag & VFS_RDONLY) != 0))
		goto zone_out;		/* return this label */

	/*
	 * Otherwise, if we're not in the global zone, use the label of
	 * our zone.
	 */
	if ((zone = curproc->p_zone) != global_zone) {
		zone_hold(zone);
		goto zone_out;		/* return this label */
	}

	/*
	 * We're in the global zone and the mount is R/W ... so the file
	 * may actually be in the global zone -- or in the root of any zone.
	 * Always build our own path for the file, to be sure it's simplified
	 * (i.e., no ".", "..", "//", and so on).
	 */

	zone_rele(zone);
	zone = zone_find_by_any_path(vpath, B_FALSE);

zone_out:
	if ((curproc->p_zone == global_zone) && (zone == global_zone)) {
		vfs_t		*nvfs;
		boolean_t	exported = B_FALSE;
		refstr_t	*mntpt_ref;
		char		*mntpt;

		/*
		 * File is in the global zone - check whether it's admin_high.
		 * If it's in a filesys that was exported from the global zone,
		 * it's admin_low by definition.  Otherwise, if it's in a
		 * filesys that's NOT exported to any zone, it's admin_high.
		 *
		 * And for these files if there wasn't a valid mount resource,
		 * the file must be admin_high (not exported, probably a global
		 * zone device).
		 */
		if (!vfs_is_held)
			goto out_high;

		mntpt_ref = vfs_getmntpoint(vfsp);
		mntpt = (char *)refstr_value(mntpt_ref);

		if ((mntpt != NULL) && (*mntpt == '/')) {
			zone_t	*to_zone;

			to_zone = zone_find_by_any_path(mntpt, B_FALSE);
			zone_rele(to_zone);
			if (to_zone != global_zone) {
				/* force admin_low */
				exported = B_TRUE;
			}
		}
		if (mntpt_ref)
			refstr_rele(mntpt_ref);

		if (!exported) {
			size_t	plen = strlen(vpath);

			vfs_list_read_lock();
			nvfs = vfsp->vfs_next;
			while (nvfs != vfsp) {
				const char	*rstr;
				size_t		rlen = 0;

				/*
				 * Skip checking this vfs if it's not lofs
				 * (the only way to export from the global
				 * zone to a zone).
				 */
				if (strncmp(vfssw[nvfs->vfs_fstype].vsw_name,
				    "lofs", 4) != 0) {
					nvfs = nvfs->vfs_next;
					continue;
				}

				rstr = refstr_value(nvfs->vfs_resource);
				if (rstr != NULL)
					rlen = strlen(rstr);

				/*
				 * Check for a match: does this vfs correspond
				 * to our global zone file path?  I.e., check
				 * if the resource string of this vfs is a
				 * prefix of our path.
				 */
				if ((rlen > 0) && (rlen <= plen) &&
				    (strncmp(rstr, vpath, rlen) == 0) &&
				    (vpath[rlen] == '/' ||
				    vpath[rlen] == '\0')) {
					/* force admin_low */
					exported = B_TRUE;
					break;
				}
				nvfs = nvfs->vfs_next;
			}
			vfs_list_unlock();
		}

		if (!exported)
			goto out_high;
	}

	if (vfs_is_held)
		VFS_RELE(vfsp);

	/*
	 * Now that we have the "home" zone for the file, return the slabel
	 * of that zone.
	 */
	zl = zone->zone_slabel;
	label_hold(zl);
	zone_rele(zone);
	return (zl);

out_high:
	if (vfs_is_held)
		VFS_RELE(vfsp);

	label_hold(l_admin_high);
	zone_rele(zone);
	return (l_admin_high);
}
Example #12
0
/*
 * Mount a file descriptor onto the node in the file system.
 * Create a new vnode, update the attributes with info from the
 * file descriptor and the mount point.  The mask, mode, uid, gid,
 * atime, mtime and ctime are taken from the mountpt.  Link count is
 * set to one, the file system id is namedev and nodeid is unique
 * for each mounted object.  Other attributes are taken from mount point.
 * Make sure user is owner (or root) with write permissions on mount point.
 * Hash the new vnode and return 0.
 * Upon entry to this routine, the file descriptor is in the
 * fd field of a struct namefd.  Copy that structure from user
 * space and retrieve the file descriptor.
 */
static int
nm_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *crp)
{
	struct namefd namefdp;
	struct vnode *filevp;		/* file descriptor vnode */
	struct file *fp;
	struct vnode *newvp;		/* vnode representing this mount */
	struct vnode *rvp;		/* realvp (if any) for the mountpt */
	struct namenode *nodep;		/* namenode for this mount */
	struct vattr filevattr;		/* attributes of file dec.  */
	struct vattr *vattrp;		/* attributes of this mount */
	char *resource_name;
	char *resource_nodetype;
	statvfs64_t *svfsp;
	int error = 0;

	/*
	 * Get the file descriptor from user space.
	 * Make sure the file descriptor is valid and has an
	 * associated file pointer.
	 * If so, extract the vnode from the file pointer.
	 */
	if (uap->datalen != sizeof (struct namefd))
		return (EINVAL);

	if (copyin(uap->dataptr, &namefdp, uap->datalen))
		return (EFAULT);

	if ((fp = getf(namefdp.fd)) == NULL)
		return (EBADF);

	/*
	 * If the mount point already has something mounted
	 * on it, disallow this mount.  (This restriction may
	 * be removed in a later release).
	 * Or unmount has completed but the namefs ROOT vnode
	 * count has not decremented to zero, disallow this mount.
	 */

	mutex_enter(&mvp->v_lock);
	if ((mvp->v_flag & VROOT) ||
	    vfs_matchops(mvp->v_vfsp, namefs_vfsops)) {
		mutex_exit(&mvp->v_lock);
		releasef(namefdp.fd);
		return (EBUSY);
	}
	mutex_exit(&mvp->v_lock);

	/*
	 * Cannot allow users to fattach() in /dev/pts.
	 * First, there is no need for doing so and secondly
	 * we cannot allow arbitrary users to park on a node in
	 * /dev/pts or /dev/vt.
	 */
	rvp = NULLVP;
	if (vn_matchops(mvp, spec_getvnodeops()) &&
	    VOP_REALVP(mvp, &rvp, NULL) == 0 && rvp &&
	    (vn_matchops(rvp, devpts_getvnodeops()) ||
	    vn_matchops(rvp, devvt_getvnodeops()))) {
		releasef(namefdp.fd);
		return (ENOTSUP);
	}

	filevp = fp->f_vnode;
	if (filevp->v_type == VDIR || filevp->v_type == VPORT) {
		releasef(namefdp.fd);
		return (EINVAL);
	}

	/*
	 * If the fd being mounted refers to neither a door nor a stream,
	 * make sure the caller is privileged.
	 */
	if (filevp->v_type != VDOOR && filevp->v_stream == NULL) {
		if (secpolicy_fs_mount(crp, filevp, vfsp) != 0) {
			/* fd is neither a stream nor a door */
			releasef(namefdp.fd);
			return (EINVAL);
		}
	}

	/*
	 * Make sure the file descriptor is not the root of some
	 * file system.
	 * If it's not, create a reference and allocate a namenode
	 * to represent this mount request.
	 */
	if (filevp->v_flag & VROOT) {
		releasef(namefdp.fd);
		return (EBUSY);
	}

	nodep = kmem_zalloc(sizeof (struct namenode), KM_SLEEP);

	mutex_init(&nodep->nm_lock, NULL, MUTEX_DEFAULT, NULL);
	vattrp = &nodep->nm_vattr;
	vattrp->va_mask = AT_ALL;
	if (error = VOP_GETATTR(mvp, vattrp, 0, crp, NULL))
		goto out;

	filevattr.va_mask = AT_ALL;
	if (error = VOP_GETATTR(filevp, &filevattr, 0, crp, NULL))
		goto out;
	/*
	 * Make sure the user is the owner of the mount point
	 * or has sufficient privileges.
	 */
	if (error = secpolicy_vnode_owner(crp, vattrp->va_uid))
		goto out;

	/*
	 * Make sure the user has write permissions on the
	 * mount point (or has sufficient privileges).
	 */
	if (!(vattrp->va_mode & VWRITE) &&
	    secpolicy_vnode_access(crp, mvp, vattrp->va_uid, VWRITE) != 0) {
		error = EACCES;
		goto out;
	}

	/*
	 * If the file descriptor has file/record locking, don't
	 * allow the mount to succeed.
	 */
	if (vn_has_flocks(filevp)) {
		error = EACCES;
		goto out;
	}

	/*
	 * Initialize the namenode.
	 */
	if (filevp->v_stream) {
		struct stdata *stp = filevp->v_stream;
		mutex_enter(&stp->sd_lock);
		stp->sd_flag |= STRMOUNT;
		mutex_exit(&stp->sd_lock);
	}
	nodep->nm_filevp = filevp;
	mutex_enter(&fp->f_tlock);
	fp->f_count++;
	mutex_exit(&fp->f_tlock);

	releasef(namefdp.fd);
	nodep->nm_filep = fp;
	nodep->nm_mountpt = mvp;

	/*
	 * The attributes for the mounted file descriptor were initialized
	 * above by applying VOP_GETATTR to the mount point.  Some of
	 * the fields of the attributes structure will be overwritten
	 * by the attributes from the file descriptor.
	 */
	vattrp->va_type    = filevattr.va_type;
	vattrp->va_fsid    = namedev;
	vattrp->va_nodeid  = namenodeno_alloc();
	vattrp->va_nlink   = 1;
	vattrp->va_size    = filevattr.va_size;
	vattrp->va_rdev    = filevattr.va_rdev;
	vattrp->va_blksize = filevattr.va_blksize;
	vattrp->va_nblocks = filevattr.va_nblocks;
	vattrp->va_seq	   = 0;

	/*
	 * Initialize new vnode structure for the mounted file descriptor.
	 */
	nodep->nm_vnode = vn_alloc(KM_SLEEP);
	newvp = NMTOV(nodep);

	newvp->v_flag = filevp->v_flag | VROOT | VNOMAP | VNOSWAP;
	vn_setops(newvp, nm_vnodeops);
	newvp->v_vfsp = vfsp;
	newvp->v_stream = filevp->v_stream;
	newvp->v_type = filevp->v_type;
	newvp->v_rdev = filevp->v_rdev;
	newvp->v_data = (caddr_t)nodep;
	VFS_HOLD(vfsp);
	vn_exists(newvp);

	/*
	 * Initialize the vfs structure.
	 */
	vfsp->vfs_vnodecovered = NULL;
	vfsp->vfs_flag |= VFS_UNLINKABLE;
	vfsp->vfs_bsize = 1024;
	vfsp->vfs_fstype = namefstype;
	vfs_make_fsid(&vfsp->vfs_fsid, namedev, namefstype);
	vfsp->vfs_data = (caddr_t)nodep;
	vfsp->vfs_dev = namedev;
	vfsp->vfs_bcount = 0;

	/*
	 * Set the name we mounted from.
	 */
	switch (filevp->v_type) {
	case VPROC:	/* VOP_GETATTR() translates this to VREG */
	case VREG:	resource_nodetype = "file"; break;
	case VDIR:	resource_nodetype = "directory"; break;
	case VBLK:	resource_nodetype = "device"; break;
	case VCHR:	resource_nodetype = "device"; break;
	case VLNK:	resource_nodetype = "link"; break;
	case VFIFO:	resource_nodetype = "fifo"; break;
	case VDOOR:	resource_nodetype = "door"; break;
	case VSOCK:	resource_nodetype = "socket"; break;
	default:	resource_nodetype = "resource"; break;
	}

#define	RESOURCE_NAME_SZ 128 /* Maximum length of the resource name */
	resource_name = kmem_alloc(RESOURCE_NAME_SZ, KM_SLEEP);
	svfsp = kmem_alloc(sizeof (statvfs64_t), KM_SLEEP);

	error = VFS_STATVFS(filevp->v_vfsp, svfsp);
	if (error == 0) {
		(void) snprintf(resource_name, RESOURCE_NAME_SZ,
		    "unspecified_%s_%s", svfsp->f_basetype, resource_nodetype);
	} else {
		(void) snprintf(resource_name, RESOURCE_NAME_SZ,
		    "unspecified_%s", resource_nodetype);
	}

	vfs_setresource(vfsp, resource_name);

	kmem_free(svfsp, sizeof (statvfs64_t));
	kmem_free(resource_name, RESOURCE_NAME_SZ);
#undef RESOURCE_NAME_SZ

	/*
	 * Insert the namenode.
	 */
	mutex_enter(&ntable_lock);
	nameinsert(nodep);
	mutex_exit(&ntable_lock);
	return (0);
out:
	releasef(namefdp.fd);
	kmem_free(nodep, sizeof (struct namenode));
	return (error);
}