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