static void smb_tree_acl_access(cred_t *cred, const char *sharename, vnode_t *pathvp, uint32_t *access) { int rc; vfs_t *vfsp; vnode_t *root = NULL; vnode_t *sharevp = NULL; char *sharepath; struct pathname pnp; size_t size; *access = ACE_ALL_PERMS; /* default to full "UNIX" access */ /* * Using the vnode of the share path, we then find the root * directory of the mounted file system. We will then look to * see if there is a .zfs/shares directory and if there is, * get the access information from the ACL/ACES values and * check against the cred. */ vfsp = pathvp->v_vfsp; if (vfsp != NULL) rc = VFS_ROOT(vfsp, &root); else rc = ENOENT; if (rc != 0) return; /* * Find the share object, if there is one. Need to construct * the path to the .zfs/shares/<sharename> object and look it * up. root is called held but will be released by * lookuppnvp(). */ size = sizeof (SHARES_DIR) + strlen(sharename) + 1; sharepath = kmem_alloc(size, KM_SLEEP); (void) sprintf(sharepath, "%s%s", SHARES_DIR, sharename); pn_alloc(&pnp); (void) pn_set(&pnp, sharepath); rc = lookuppnvp(&pnp, NULL, NO_FOLLOW, NULL, &sharevp, rootdir, root, kcred); pn_free(&pnp); kmem_free(sharepath, size); /* * Now get the effective access value based on cred and ACL * values. */ if (rc == 0) { smb_vop_eaccess(sharevp, (int *)access, V_ACE_MASK, NULL, cred); VN_RELE(sharevp); } }
vnode_t * smb_lookuppathvptovp(smb_request_t *sr, char *path, vnode_t *startvp, vnode_t *rootvp) { pathname_t pn; vnode_t *vp = NULL; int lookup_flags = FOLLOW; if (SMB_TREE_IS_CASEINSENSITIVE(sr)) lookup_flags |= FIGNORECASE; (void) pn_alloc(&pn); if (pn_set(&pn, path) == 0) { VN_HOLD(startvp); if (rootvp != rootdir) VN_HOLD(rootvp); /* lookuppnvp should release the holds */ if (lookuppnvp(&pn, NULL, lookup_flags, NULL, &vp, rootvp, startvp, kcred) != 0) { pn_free(&pn); return (NULL); } } pn_free(&pn); return (vp); }
/* * Look up a logical name in the global zone. * Provides the ability to map the global zone's device name * to an alternate name within a zone. The primary example * is the virtual console device /dev/zcons/[zonename]/zconsole * mapped to /[zonename]/root/dev/zconsole. */ static void prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir, char *name, char *rename) { int error; struct vnode *avp, *gdv, *gddv; struct sdev_node *newdv; struct vattr vattr = {0}; struct pathname pn; /* check if node already exists */ newdv = sdev_cache_lookup(dir, rename); if (newdv) { ASSERT(newdv->sdev_state != SDEV_ZOMBIE); SDEV_SIMPLE_RELE(newdv); return; } /* sanity check arguments */ if (!gdir || pn_get(name, UIO_SYSSPACE, &pn)) return; /* perform a relative lookup of the global /dev instance */ gddv = SDEVTOV(gdir); VN_HOLD(gddv); error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv, rootdir, gddv, kcred); pn_free(&pn); if (error) { sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name)); return; } ASSERT(gdv && gdv->v_type != VLNK); /* * Found the entry in global /dev, figure out attributes * by looking at backing store. Call into devfs for default. * Note, mapped device is persisted under the new name */ prof_getattr(dir, rename, gdv, &vattr, &avp, NULL); if (gdv->v_type != VDIR) { VN_RELE(gdv); gdir = NULL; } else gdir = VTOSDEV(gdv); if (prof_mknode(dir, rename, &newdv, &vattr, avp, (void *)gdir, kcred) == 0) { ASSERT(newdv->sdev_state != SDEV_ZOMBIE); SDEV_SIMPLE_RELE(newdv); } }
/* * Last chance for a zone to see a node. If our parent dir is * SDEV_ZONED, then we look up the "zone" property for the node. If the * property is found and matches the current zone name, we allow it. * Note that this isn't quite correct for the global zone peeking inside * a zone's /dev - for that to work, we'd have to have a per-dev-mount * zone ref squirreled away. */ static int prof_zone_matched(char *name, struct sdev_node *dir) { vnode_t *gvn = SDEVTOV(dir->sdev_origin); struct pathname pn; vnode_t *vn = NULL; char zonename[ZONENAME_MAX]; int znlen = ZONENAME_MAX; int ret; ASSERT((dir->sdev_flags & SDEV_ZONED) != 0); sdcmn_err10(("sdev_node %p is zoned, looking for %s\n", (void *)dir, name)); if (pn_get(name, UIO_SYSSPACE, &pn)) return (0); VN_HOLD(gvn); ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred); pn_free(&pn); if (ret != 0) { sdcmn_err10(("prof_zone_matched: %s not found\n", name)); return (0); } /* * VBLK doesn't matter, and the property name is in fact treated * as a const char *. */ ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone", DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen); VN_RELE(vn); if (ret == DDI_PROP_NOT_FOUND) { sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn)); return (0); } else if (ret != DDI_PROP_SUCCESS) { sdcmn_err10(("vnode %p: zone prop error: %d\n", (void *)vn, ret)); return (0); } sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename)); return (strcmp(zonename, curproc->p_zone->zone_name) == 0); }
/* * Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp() * and will be released within lookuppnvp(). */ static int smb_pathname_lookup(pathname_t *pn, pathname_t *rpn, int flags, vnode_t **vp, vnode_t *rootvp, vnode_t *dvp, smb_attr_t *attr, cred_t *cred) { int err; *vp = NULL; VN_HOLD(dvp); if (rootvp != rootdir) VN_HOLD(rootvp); err = lookuppnvp(pn, rpn, flags, NULL, vp, rootvp, dvp, cred); if ((err == 0) && (attr != NULL)) (void) smb_vop_getattr(*vp, NULL, attr, 0, kcred); return (err); }
/* * Lookup the user file name from a given vp, using a specific credential. */ int lookuppnatcred( struct pathname *pnp, /* pathname to lookup */ struct pathname *rpnp, /* if non-NULL, return resolved path */ int followlink, /* (don't) follow sym links */ vnode_t **dirvpp, /* ptr for parent vnode */ vnode_t **compvpp, /* ptr for entry vnode */ vnode_t *startvp, /* start search from this vp */ cred_t *cr) /* user credential */ { vnode_t *vp; /* current directory vp */ vnode_t *rootvp; proc_t *p = curproc; if (pnp->pn_pathlen == 0) return (ENOENT); mutex_enter(&p->p_lock); /* for u_rdir and u_cdir */ if ((rootvp = PTOU(p)->u_rdir) == NULL) rootvp = rootdir; else if (rootvp != rootdir) /* no need to VN_HOLD rootdir */ VN_HOLD(rootvp); if (pnp->pn_path[0] == '/') { vp = rootvp; } else { vp = (startvp == NULL) ? PTOU(p)->u_cdir : startvp; } VN_HOLD(vp); mutex_exit(&p->p_lock); /* * Skip over leading slashes */ if (pnp->pn_path[0] == '/') { do { pnp->pn_path++; pnp->pn_pathlen--; } while (pnp->pn_path[0] == '/'); } return (lookuppnvp(pnp, rpnp, followlink, dirvpp, compvpp, rootvp, vp, cr)); }
void * osi_UfsOpen(afs_dcache_id_t *ainode) { #ifdef AFS_CACHE_VNODE_PATH struct vnode *vp; #else struct inode *ip; #endif struct osi_file *afile = NULL; afs_int32 code = 0; int dummy; #ifdef AFS_CACHE_VNODE_PATH char namebuf[1024]; struct pathname lookpn; #endif struct osi_stat tstat; afile = osi_AllocSmallSpace(sizeof(struct osi_file)); AFS_GUNLOCK(); /* * AFS_CACHE_VNODE_PATH can be used with any file system, including ZFS or tmpfs. * The ainode is not an inode number but a path. */ #ifdef AFS_CACHE_VNODE_PATH /* Can not use vn_open or lookupname, they use user's CRED() * We need to run as root So must use low level lookuppnvp * assume fname starts with / */ code = pn_get_buf(ainode->ufs, AFS_UIOSYS, &lookpn, namebuf, sizeof(namebuf)); if (code != 0) osi_Panic("UfsOpen: pn_get_buf failed %ld %s", code, ainode->ufs); VN_HOLD(rootdir); /* released in loopuppnvp */ code = lookuppnvp(&lookpn, NULL, FOLLOW, NULL, &vp, rootdir, rootdir, afs_osi_credp); if (code != 0) osi_Panic("UfsOpen: lookuppnvp failed %ld %s", code, ainode->ufs); #ifdef AFS_SUN511_ENV code = VOP_OPEN(&vp, FREAD|FWRITE, afs_osi_credp, NULL); #else code = VOP_OPEN(&vp, FREAD|FWRITE, afs_osi_credp); #endif if (code != 0) osi_Panic("UfsOpen: VOP_OPEN failed %ld %s", code, ainode->ufs); #else code = igetinode(afs_cacheVfsp, (dev_t) cacheDev.dev, ainode->ufs, &ip, CRED(), &dummy); #endif AFS_GLOCK(); if (code) { osi_FreeSmallSpace(afile); osi_Panic("UfsOpen: igetinode failed %ld %s", code, ainode->ufs); } #ifdef AFS_CACHE_VNODE_PATH afile->vnode = vp; code = afs_osi_Stat(afile, &tstat); afile->size = tstat.size; #else afile->vnode = ITOV(ip); afile->size = VTOI(afile->vnode)->i_size; #endif afile->offset = 0; afile->proc = (int (*)())0; return (void *)afile; }
/* * Given a directory, return the full, resolved path. This looks up "..", * searches for the given vnode in the parent, appends the component, etc. It * is used to implement vnodetopath() and getcwd() when the cached path fails. */ static int dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags, cred_t *cr) { pathname_t pn, rpn, emptypn; vnode_t *cmpvp, *pvp = NULL; vnode_t *startvp = vp; int err = 0, vprivs; size_t complen; char *dbuf; dirent64_t *dp; char *bufloc; size_t dlen = DIRENT64_RECLEN(MAXPATHLEN); refstr_t *mntpt; /* Operation only allowed on directories */ ASSERT(vp->v_type == VDIR); /* We must have at least enough space for "/" */ if (buflen < 2) return (ENAMETOOLONG); /* Start at end of string with terminating null */ bufloc = &buf[buflen - 1]; *bufloc = '\0'; pn_alloc(&pn); pn_alloc(&rpn); dbuf = kmem_alloc(dlen, KM_SLEEP); bzero(&emptypn, sizeof (emptypn)); /* * Begin with an additional reference on vp. This will be decremented * during the loop. */ VN_HOLD(vp); for (;;) { /* * Return if we've reached the root. If the buffer is empty, * return '/'. We explicitly don't use vn_compare(), since it * compares the real vnodes. A lofs mount of '/' would produce * incorrect results otherwise. */ if (VN_CMP(vrootp, vp)) { if (*bufloc == '\0') *--bufloc = '/'; break; } /* * If we've reached the VFS root, something has gone wrong. We * should have reached the root in the above check. The only * explantation is that 'vp' is not contained withing the given * root, in which case we return EPERM. */ if (VN_CMP(rootdir, vp)) { err = EPERM; goto out; } /* * Shortcut: see if this vnode is a mountpoint. If so, * grab the path information from the vfs_t. */ if (vp->v_flag & VROOT) { mntpt = vfs_getmntpoint(vp->v_vfsp); if ((err = pn_set(&pn, (char *)refstr_value(mntpt))) == 0) { refstr_rele(mntpt); rpn.pn_path = rpn.pn_buf; /* * Ensure the mountpoint still exists. */ VN_HOLD(vrootp); if (vrootp != rootdir) VN_HOLD(vrootp); if (lookuppnvp(&pn, &rpn, flags, NULL, &cmpvp, vrootp, vrootp, cr) == 0) { if (VN_CMP(vp, cmpvp)) { VN_RELE(cmpvp); complen = strlen(rpn.pn_path); bufloc -= complen; if (bufloc < buf) { err = ERANGE; goto out; } bcopy(rpn.pn_path, bufloc, complen); break; } else { VN_RELE(cmpvp); } } } else { refstr_rele(mntpt); } } /* * Shortcut: see if this vnode has correct v_path. If so, * we have the work done. */ mutex_enter(&vp->v_lock); if (vp->v_path != NULL) { if ((err = pn_set(&pn, vp->v_path)) == 0) { mutex_exit(&vp->v_lock); rpn.pn_path = rpn.pn_buf; /* * Ensure the v_path pointing to correct vnode */ VN_HOLD(vrootp); if (vrootp != rootdir) VN_HOLD(vrootp); if (lookuppnvp(&pn, &rpn, flags, NULL, &cmpvp, vrootp, vrootp, cr) == 0) { if (VN_CMP(vp, cmpvp)) { VN_RELE(cmpvp); complen = strlen(rpn.pn_path); bufloc -= complen; if (bufloc < buf) { err = ERANGE; goto out; } bcopy(rpn.pn_path, bufloc, complen); break; } else { VN_RELE(cmpvp); } } } else { mutex_exit(&vp->v_lock); } } else { mutex_exit(&vp->v_lock); } /* * Shortcuts failed, search for this vnode in its parent. If * this is a mountpoint, then get the vnode underneath. */ if (vp->v_flag & VROOT) vp = vn_under(vp); if ((err = VOP_LOOKUP(vp, "..", &pvp, &emptypn, 0, vrootp, cr, NULL, NULL, NULL)) != 0) goto out; /* * With extended attributes, it's possible for a directory to * have a parent that is a regular file. Check for that here. */ if (pvp->v_type != VDIR) { err = ENOTDIR; goto out; } /* * If this is true, something strange has happened. This is * only true if we are the root of a filesystem, which should * have been caught by the check above. */ if (VN_CMP(pvp, vp)) { err = ENOENT; goto out; } /* * Check if we have read and search privilege so, that * we can lookup the path in the directory */ vprivs = (flags & LOOKUP_CHECKREAD) ? VREAD | VEXEC : VEXEC; if ((err = VOP_ACCESS(pvp, vprivs, 0, cr, NULL)) != 0) { goto out; } /* * Try to obtain the path component from dnlc cache * before searching through the directory. */ if ((cmpvp = dnlc_reverse_lookup(vp, dbuf, dlen)) != NULL) { /* * If we got parent vnode as a result, * then the answered path is correct. */ if (VN_CMP(cmpvp, pvp)) { VN_RELE(cmpvp); complen = strlen(dbuf); bufloc -= complen; if (bufloc <= buf) { err = ENAMETOOLONG; goto out; } bcopy(dbuf, bufloc, complen); /* Prepend a slash to the current path */ *--bufloc = '/'; /* And continue with the next component */ VN_RELE(vp); vp = pvp; pvp = NULL; continue; } else { VN_RELE(cmpvp); } } /* * Search the parent directory for the entry corresponding to * this vnode. */ if ((err = dirfindvp(vrootp, pvp, vp, cr, dbuf, dlen, &dp)) != 0) goto out; complen = strlen(dp->d_name); bufloc -= complen; if (bufloc <= buf) { err = ENAMETOOLONG; goto out; } bcopy(dp->d_name, bufloc, complen); /* Prepend a slash to the current path. */ *--bufloc = '/'; /* And continue with the next component */ VN_RELE(vp); vp = pvp; pvp = NULL; } /* * Place the path at the beginning of the buffer. */ if (bufloc != buf) ovbcopy(bufloc, buf, buflen - (bufloc - buf)); out: /* * If the error was ESTALE and the current directory to look in * was the root for this lookup, the root for a mounted file * system, or the starting directory for lookups, then * return ENOENT instead of ESTALE. In this case, no recovery * is possible by the higher level. If ESTALE was returned for * some intermediate directory along the path, then recovery * is potentially possible and retrying from the higher level * will either correct the situation by purging stale cache * entries or eventually get back to the point where no recovery * is possible. */ if (err == ESTALE && (VN_CMP(vp, vrootp) || (vp->v_flag & VROOT) || vp == startvp)) err = ENOENT; kmem_free(dbuf, dlen); VN_RELE(vp); if (pvp) VN_RELE(pvp); pn_free(&pn); pn_free(&rpn); 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); }