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); }
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); } }
/* * given an absolute pathname, convert it, if possible, to a devfs * name. Examples: * /dev/rsd3a to /pci@1f,4000/glm@3/sd@0,0:a * /dev/dsk/c0t0d0s0 to /pci@1f,4000/glm@3/sd@0,0:a * /devices/pci@1f,4000/glm@3/sd@0,0:a to /pci@1f,4000/glm@3/sd@0,0:a * /pci@1f,4000/glm@3/sd@0,0:a unchanged * * This routine deals with symbolic links, physical pathname with and * without /devices stripped. Returns 0 on success or -1 on failure. */ static int resolve_devfs_name(char *name, char *buffer) { int error; char *fullname = NULL; struct pathname pn, rpn; /* if not a /dev or /device name, prepend /devices */ if (strncmp(name, "/dev/", 5) != 0 && strncmp(name, "/devices/", 9) != 0) { fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP); (void) snprintf(fullname, MAXPATHLEN, "/devices%s", name); name = fullname; } if (pn_get(name, UIO_SYSSPACE, &pn) != 0) { if (fullname) kmem_free(fullname, MAXPATHLEN); return (-1); } pn_alloc(&rpn); error = lookuppn(&pn, &rpn, FOLLOW, NULL, NULL); if (error == 0) bcopy(rpn.pn_path, buffer, rpn.pn_pathlen); pn_free(&pn); pn_free(&rpn); if (fullname) kmem_free(fullname, MAXPATHLEN); return (error); }
int smb_pathname(smb_request_t *sr, char *path, int flags, smb_node_t *root_node, smb_node_t *cur_node, smb_node_t **dir_node, smb_node_t **ret_node, cred_t *cred) { char *component, *real_name, *namep; pathname_t pn, rpn, upn, link_pn; smb_node_t *dnode, *fnode; smb_attr_t attr; vnode_t *rootvp, *vp; size_t pathleft; int err = 0; int nlink = 0; int local_flags; uint32_t abe_flag = 0; char namebuf[MAXNAMELEN]; if (path == NULL) return (EINVAL); ASSERT(root_node); ASSERT(cur_node); ASSERT(ret_node); *ret_node = NULL; if (dir_node) *dir_node = NULL; (void) pn_alloc(&upn); if ((err = pn_set(&upn, path)) != 0) { (void) pn_free(&upn); return (err); } if (SMB_TREE_SUPPORTS_ABE(sr)) abe_flag = SMB_ABE; (void) pn_alloc(&pn); (void) pn_alloc(&rpn); component = kmem_alloc(MAXNAMELEN, KM_SLEEP); real_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); fnode = NULL; dnode = cur_node; smb_node_ref(dnode); rootvp = root_node->vp; while ((pathleft = pn_pathleft(&upn)) != 0) { if (fnode) { smb_node_release(dnode); dnode = fnode; fnode = NULL; } if ((err = pn_getcomponent(&upn, component)) != 0) break; if ((namep = smb_pathname_catia_v5tov4(sr, component, namebuf, sizeof (namebuf))) == NULL) { err = EILSEQ; break; } if ((err = pn_set(&pn, namep)) != 0) break; local_flags = flags & FIGNORECASE; err = smb_pathname_lookup(&pn, &rpn, local_flags, &vp, rootvp, dnode->vp, &attr, cred); if (err) { if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) || !smb_maybe_mangled(component)) break; if ((err = smb_unmangle(dnode, component, real_name, MAXNAMELEN, abe_flag)) != 0) break; if ((namep = smb_pathname_catia_v5tov4(sr, real_name, namebuf, sizeof (namebuf))) == NULL) { err = EILSEQ; break; } if ((err = pn_set(&pn, namep)) != 0) break; local_flags = 0; err = smb_pathname_lookup(&pn, &rpn, local_flags, &vp, rootvp, dnode->vp, &attr, cred); if (err) break; } /* * This check MUST be done before symlink check * since a reparse point is of type VLNK but should * not be handled like a regular symlink. */ if (attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) { err = EREMOTE; VN_RELE(vp); break; } if ((vp->v_type == VLNK) && ((flags & FOLLOW) || pn_pathleft(&upn))) { if (++nlink > MAXSYMLINKS) { err = ELOOP; VN_RELE(vp); break; } (void) pn_alloc(&link_pn); err = pn_getsymlink(vp, &link_pn, cred); VN_RELE(vp); if (err == 0) { if (pn_pathleft(&link_pn) == 0) (void) pn_set(&link_pn, "."); err = pn_insert(&upn, &link_pn, strlen(component)); } pn_free(&link_pn); if (err) break; if (upn.pn_pathlen == 0) { err = ENOENT; break; } if (upn.pn_path[0] == '/') { fnode = root_node; smb_node_ref(fnode); } if (pn_fixslash(&upn)) flags |= FOLLOW; } else { if (flags & FIGNORECASE) { if (strcmp(rpn.pn_path, "/") != 0) pn_setlast(&rpn); namep = rpn.pn_path; } else { namep = pn.pn_path; } namep = smb_pathname_catia_v4tov5(sr, namep, namebuf, sizeof (namebuf)); fnode = smb_node_lookup(sr, NULL, cred, vp, namep, dnode, NULL); VN_RELE(vp); if (fnode == NULL) { err = ENOMEM; break; } } while (upn.pn_path[0] == '/') { upn.pn_path++; upn.pn_pathlen--; } } if ((pathleft) && (err == ENOENT)) err = ENOTDIR; if (err) { if (fnode) smb_node_release(fnode); if (dnode) smb_node_release(dnode); } else { *ret_node = fnode; if (dir_node) *dir_node = dnode; else smb_node_release(dnode); } kmem_free(component, MAXNAMELEN); kmem_free(real_name, MAXNAMELEN); (void) pn_free(&pn); (void) pn_free(&rpn); (void) pn_free(&upn); return (err); }
int smb_pathname_reduce( smb_request_t *sr, cred_t *cred, const char *path, smb_node_t *share_root_node, smb_node_t *cur_node, smb_node_t **dir_node, char *last_component) { smb_node_t *root_node; pathname_t ppn; char *usepath; int lookup_flags = FOLLOW; int trailing_slash = 0; int err = 0; int len; smb_node_t *vss_cur_node; smb_node_t *vss_root_node; smb_node_t *local_cur_node; smb_node_t *local_root_node; ASSERT(dir_node); ASSERT(last_component); *dir_node = NULL; *last_component = '\0'; vss_cur_node = NULL; vss_root_node = NULL; if (sr && sr->tid_tree) { if (STYPE_ISIPC(sr->tid_tree->t_res_type)) return (EACCES); } if (SMB_TREE_IS_CASEINSENSITIVE(sr)) lookup_flags |= FIGNORECASE; if (path == NULL) return (EINVAL); if (*path == '\0') return (ENOENT); usepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); if ((len = strlcpy(usepath, path, MAXPATHLEN)) >= MAXPATHLEN) { kmem_free(usepath, MAXPATHLEN); return (ENAMETOOLONG); } (void) strsubst(usepath, '\\', '/'); if (share_root_node) root_node = share_root_node; else root_node = sr->sr_server->si_root_smb_node; if (cur_node == NULL) cur_node = root_node; local_cur_node = cur_node; local_root_node = root_node; if (SMB_TREE_IS_DFSROOT(sr) && (sr->smb_flg2 & SMB_FLAGS2_DFS)) { err = smb_pathname_dfs_preprocess(sr, usepath, MAXPATHLEN); if (err != 0) { kmem_free(usepath, MAXPATHLEN); return (err); } len = strlen(usepath); } if (sr && (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH)) { err = smb_vss_lookup_nodes(sr, root_node, cur_node, usepath, &vss_cur_node, &vss_root_node); if (err != 0) { kmem_free(usepath, MAXPATHLEN); return (err); } len = strlen(usepath); local_cur_node = vss_cur_node; local_root_node = vss_root_node; } if (usepath[len - 1] == '/') trailing_slash = 1; (void) strcanon(usepath, "/"); (void) pn_alloc(&ppn); if ((err = pn_set(&ppn, usepath)) != 0) { (void) pn_free(&ppn); kmem_free(usepath, MAXPATHLEN); if (vss_cur_node != NULL) (void) smb_node_release(vss_cur_node); if (vss_root_node != NULL) (void) smb_node_release(vss_root_node); return (err); } /* * If a path does not have a trailing slash, strip off the * last component. (We only need to return an smb_node for * the second to last component; a name is returned for the * last component.) */ if (trailing_slash) { (void) strlcpy(last_component, ".", MAXNAMELEN); } else { (void) pn_setlast(&ppn); (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN); ppn.pn_path[0] = '\0'; } if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) { smb_node_ref(local_cur_node); *dir_node = local_cur_node; } else { err = smb_pathname(sr, ppn.pn_buf, lookup_flags, local_root_node, local_cur_node, NULL, dir_node, cred); } (void) pn_free(&ppn); kmem_free(usepath, MAXPATHLEN); /* * Prevent traversal to another file system if mount point * traversal is disabled. * * Note that we disregard whether the traversal of the path went * outside of the file system and then came back (say via a link). * This means that only symlinks that are expressed relatively to * the share root work. * * share_root_node is NULL when mapping a share, so we disregard * that case. */ if ((err == 0) && share_root_node) { if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) { err = EACCES; if ((sr) && (sr)->tid_tree && smb_tree_has_feature((sr)->tid_tree, SMB_TREE_TRAVERSE_MOUNTS)) err = 0; } } if (err) { if (*dir_node) { (void) smb_node_release(*dir_node); *dir_node = NULL; } *last_component = 0; } if (vss_cur_node != NULL) (void) smb_node_release(vss_cur_node); if (vss_root_node != NULL) (void) smb_node_release(vss_root_node); return (err); }
int intpexec( struct vnode *vp, struct execa *uap, struct uarg *args, struct intpdata *idatap, int level, long *execsz, int setid, caddr_t exec_file, struct cred *cred, int brand_action) { _NOTE(ARGUNUSED(brand_action)) vnode_t *nvp; int error = 0; struct intpdata idata; struct pathname intppn; struct pathname resolvepn; char *opath; char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */ int fd = -1; if (level) { /* Can't recurse */ error = ENOEXEC; goto bad; } ASSERT(idatap == (struct intpdata *)NULL); /* * Allocate a buffer to read in the interpreter pathname. */ idata.intp = kmem_alloc(INTPSZ, KM_SLEEP); if (error = getintphead(vp, &idata)) goto fail; /* * Look the new vnode up. */ if (error = pn_get(idata.intp_name, UIO_SYSSPACE, &intppn)) goto fail; pn_alloc(&resolvepn); if (error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp)) { pn_free(&resolvepn); pn_free(&intppn); goto fail; } opath = args->pathname; args->pathname = resolvepn.pn_path; /* don't free resolvepn until we are done with args */ pn_free(&intppn); /* * When we're executing a set-uid script resulting in uids * mismatching or when we execute with additional privileges, * we close the "replace script between exec and open by shell" * hole by passing the script as /dev/fd parameter. */ if ((setid & EXECSETID_PRIVS) != 0 || (setid & (EXECSETID_UGIDS|EXECSETID_SETID)) == (EXECSETID_UGIDS|EXECSETID_SETID)) { (void) strcpy(devfd, "/dev/fd/"); if (error = execopen(&vp, &fd)) goto done; numtos(fd, &devfd[8]); args->fname = devfd; } error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred, EBA_NONE); done: VN_RELE(nvp); args->pathname = opath; pn_free(&resolvepn); fail: kmem_free(idata.intp, INTPSZ); if (error && fd != -1) (void) execclose(fd); bad: return (error); }
/* * Create a minor node for the specified volume. */ int zvol_create_minor(zfs_cmd_t *zc) { char *name = zc->zc_name; dev_t dev = zc->zc_dev; zvol_state_t *zv; objset_t *os; uint64_t volsize; minor_t minor = 0; struct pathname linkpath; int ds_mode = DS_MODE_PRIMARY; vnode_t *vp = NULL; char *devpath; size_t devpathlen = strlen(ZVOL_FULL_DEV_DIR) + 1 + strlen(name) + 1; char chrbuf[30], blkbuf[30]; int error; mutex_enter(&zvol_state_lock); if ((zv = zvol_minor_lookup(name)) != NULL) { mutex_exit(&zvol_state_lock); return (EEXIST); } if (strchr(name, '@') != 0) ds_mode |= DS_MODE_READONLY; error = dmu_objset_open(name, DMU_OST_ZVOL, ds_mode, &os); if (error) { mutex_exit(&zvol_state_lock); return (error); } error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize); if (error) { dmu_objset_close(os); mutex_exit(&zvol_state_lock); return (error); } /* * If there's an existing /dev/zvol symlink, try to use the * same minor number we used last time. */ devpath = kmem_alloc(devpathlen, KM_SLEEP); (void) sprintf(devpath, "%s/%s", ZVOL_FULL_DEV_DIR, name); error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULL, &vp); kmem_free(devpath, devpathlen); if (error == 0 && vp->v_type != VLNK) error = EINVAL; if (error == 0) { pn_alloc(&linkpath); error = pn_getsymlink(vp, &linkpath, kcred); if (error == 0) { char *ms = strstr(linkpath.pn_path, ZVOL_PSEUDO_DEV); if (ms != NULL) { ms += strlen(ZVOL_PSEUDO_DEV); minor = stoi(&ms); } } pn_free(&linkpath); } if (vp != NULL) VN_RELE(vp); /* * If we found a minor but it's already in use, we must pick a new one. */ if (minor != 0 && ddi_get_soft_state(zvol_state, minor) != NULL) minor = 0; if (minor == 0) minor = zvol_minor_alloc(); if (minor == 0) { dmu_objset_close(os); mutex_exit(&zvol_state_lock); return (ENXIO); } if (ddi_soft_state_zalloc(zvol_state, minor) != DDI_SUCCESS) { dmu_objset_close(os); mutex_exit(&zvol_state_lock); return (EAGAIN); } (void) ddi_prop_update_string(minor, zfs_dip, ZVOL_PROP_NAME, name); (void) sprintf(chrbuf, "%uc,raw", minor); if (ddi_create_minor_node(zfs_dip, chrbuf, S_IFCHR, minor, DDI_PSEUDO, 0) == DDI_FAILURE) { ddi_soft_state_free(zvol_state, minor); dmu_objset_close(os); mutex_exit(&zvol_state_lock); return (EAGAIN); } (void) sprintf(blkbuf, "%uc", minor); if (ddi_create_minor_node(zfs_dip, blkbuf, S_IFBLK, minor, DDI_PSEUDO, 0) == DDI_FAILURE) { ddi_remove_minor_node(zfs_dip, chrbuf); ddi_soft_state_free(zvol_state, minor); dmu_objset_close(os); mutex_exit(&zvol_state_lock); return (EAGAIN); } zv = ddi_get_soft_state(zvol_state, minor); (void) strcpy(zv->zv_name, name); zv->zv_min_bs = DEV_BSHIFT; zv->zv_minor = minor; zv->zv_volsize = volsize; zv->zv_objset = os; zv->zv_mode = ds_mode; zv->zv_zilog = zil_open(os, NULL); rw_init(&zv->zv_dslock, NULL, RW_DEFAULT, NULL); zil_replay(os, zv, &zv->zv_txg_assign, zvol_replay_vector, NULL); zvol_size_changed(zv, dev); /* XXX this should handle the possible i/o error */ VERIFY(dsl_prop_register(dmu_objset_ds(zv->zv_objset), "readonly", zvol_readonly_changed_cb, zv) == 0); zvol_minors++; mutex_exit(&zvol_state_lock); return (0); }
zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) #endif { cred_t *cr = CRED(); struct inode *ip; int error; fstrans_cookie_t cookie; pathname_t *ppn = NULL; pathname_t pn; int zfs_flags = 0; zfs_sb_t *zsb = dentry->d_sb->s_fs_info; if (dlen(dentry) > ZFS_MAX_DATASET_NAME_LEN) return (ERR_PTR(-ENAMETOOLONG)); crhold(cr); cookie = spl_fstrans_mark(); /* If we are a case insensitive fs, we need the real name */ if (zsb->z_case == ZFS_CASE_INSENSITIVE) { zfs_flags = FIGNORECASE; pn_alloc(&pn); ppn = &pn; } error = -zfs_lookup(dir, dname(dentry), &ip, zfs_flags, cr, NULL, ppn); spl_fstrans_unmark(cookie); ASSERT3S(error, <=, 0); crfree(cr); spin_lock(&dentry->d_lock); dentry->d_time = jiffies; #ifndef HAVE_S_D_OP d_set_d_op(dentry, &zpl_dentry_operations); #endif /* HAVE_S_D_OP */ spin_unlock(&dentry->d_lock); if (error) { /* * If we have a case sensitive fs, we do not want to * insert negative entries, so return NULL for ENOENT. * Fall through if the error is not ENOENT. Also free memory. */ if (ppn) { pn_free(ppn); if (error == -ENOENT) return (NULL); } if (error == -ENOENT) return (d_splice_alias(NULL, dentry)); else return (ERR_PTR(error)); } /* * If we are case insensitive, call the correct function * to install the name. */ if (ppn) { struct dentry *new_dentry; struct qstr ci_name; ci_name.name = pn.pn_buf; ci_name.len = strlen(pn.pn_buf); new_dentry = d_add_ci(dentry, ip, &ci_name); pn_free(ppn); return (new_dentry); } else { return (d_splice_alias(ip, dentry)); } }
/*ARGSUSED3*/ static int javaexec(vnode_t *vp, struct execa *uap, struct uarg *args, struct intpdata *idatap, int level, long *execsz, int setid, caddr_t execfile, cred_t *cred, int brand_action) { struct intpdata idata; int error; ssize_t resid; vnode_t *nvp; off_t xoff, xoff_end; char lochdr[LOCHDRSIZ]; struct pathname lookpn; struct pathname resolvepn; char *opath; if (level) return (ENOEXEC); /* no recursion */ /* * Read in the full local file header, and validate * the initial signature. */ if ((error = vn_rdwr(UIO_READ, vp, lochdr, sizeof (lochdr), 0, UIO_SYSSPACE, 0, (rlim64_t)0, cred, &resid)) != 0) return (error); if (resid != 0 || strncmp(lochdr, LOCSIG, SIGSIZ) != 0) return (ENOEXEC); /* * Ok, so this -is- a ZIP file, and might even be a JAR file. * Is it a Java executable? */ xoff = sizeof (lochdr) + LOCNAM(lochdr); xoff_end = xoff + LOCEXT(lochdr); while (xoff < xoff_end) { char xfhdr[XFHSIZ]; if ((error = vn_rdwr(UIO_READ, vp, xfhdr, sizeof (xfhdr), xoff, UIO_SYSSPACE, 0, (rlim64_t)0, cred, &resid)) != 0) return (error); if (resid != 0) return (ENOEXEC); if (XFHID(xfhdr) == XFJAVASIG) break; xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr); } if (xoff >= xoff_end) return (ENOEXEC); /* * Note: If we ever make setid execution work, we need to ensure * that we use /dev/fd to avoid the classic setuid shell script * security hole. */ if (setid) return (EACCES); /* * Find and invoke the Java runtime environment on the file */ idata.intp = NULL; idata.intp_name[0] = jexec; idata.intp_arg[0] = jexec_arg; if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &lookpn)) return (error); pn_alloc(&resolvepn); if (error = lookuppn(&lookpn, &resolvepn, FOLLOW, NULLVPP, &nvp)) { pn_free(&resolvepn); pn_free(&lookpn); return (ENOEXEC); } opath = args->pathname; args->pathname = resolvepn.pn_path; /* don't free resolvepn until we are done with args */ pn_free(&lookpn); error = gexec(&nvp, uap, args, &idata, level + 1, execsz, execfile, cred, EBA_NONE); if (!error) { /* * Close this Java executable as the interpreter * will open and close it later on. */ (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, cred, NULL); } VN_RELE(nvp); args->pathname = opath; pn_free(&resolvepn); return (error); }
/* * 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); }
/* * Starting at current directory, translate pathname pnp to end. * Leave pathname of final component in pnp, return the vnode * for the final component in *compvpp, and return the vnode * for the parent of the final component in dirvpp. * * This is the central routine in pathname translation and handles * multiple components in pathnames, separating them at /'s. It also * implements mounted file systems and processes symbolic links. * * vp is the vnode where the directory search should start. * * Reference counts: vp must be held prior to calling this function. rootvp * should only be held if rootvp != rootdir. */ int lookuppnvp( struct pathname *pnp, /* pathname to lookup */ struct pathname *rpnp, /* if non-NULL, return resolved path */ int flags, /* follow symlinks */ vnode_t **dirvpp, /* ptr for parent vnode */ vnode_t **compvpp, /* ptr for entry vnode */ vnode_t *rootvp, /* rootvp */ vnode_t *vp, /* directory to start search at */ cred_t *cr) /* user's credential */ { vnode_t *cvp; /* current component vp */ char component[MAXNAMELEN]; /* buffer for component (incl null) */ int error; int nlink; int lookup_flags; struct pathname presrvd; /* case preserved name */ struct pathname *pp = NULL; vnode_t *startvp; vnode_t *zonevp = curproc->p_zone->zone_rootvp; /* zone root */ int must_be_directory = 0; boolean_t retry_with_kcred; uint32_t auditing = AU_AUDITING(); CPU_STATS_ADDQ(CPU, sys, namei, 1); nlink = 0; cvp = NULL; if (rpnp) rpnp->pn_pathlen = 0; lookup_flags = dirvpp ? LOOKUP_DIR : 0; if (flags & FIGNORECASE) { lookup_flags |= FIGNORECASE; pn_alloc(&presrvd); pp = &presrvd; } if (auditing) audit_anchorpath(pnp, vp == rootvp); /* * Eliminate any trailing slashes in the pathname. * If there are any, we must follow all symlinks. * Also, we must guarantee that the last component is a directory. */ if (pn_fixslash(pnp)) { flags |= FOLLOW; must_be_directory = 1; } startvp = vp; next: retry_with_kcred = B_FALSE; /* * Make sure we have a directory. */ if (vp->v_type != VDIR) { error = ENOTDIR; goto bad; } if (rpnp && VN_CMP(vp, rootvp)) (void) pn_set(rpnp, "/"); /* * Process the next component of the pathname. */ if (error = pn_getcomponent(pnp, component)) { goto bad; } /* * Handle "..": two special cases. * 1. If we're at the root directory (e.g. after chroot or * zone_enter) then change ".." to "." so we can't get * out of this subtree. * 2. If this vnode is the root of a mounted file system, * then replace it with the vnode that was mounted on * so that we take the ".." in the other file system. */ if (component[0] == '.' && component[1] == '.' && component[2] == 0) { checkforroot: if (VN_CMP(vp, rootvp) || VN_CMP(vp, zonevp)) { component[1] = '\0'; } else if (vp->v_flag & VROOT) { vfs_t *vfsp; cvp = vp; /* * While we deal with the vfs pointer from the vnode * the filesystem could have been forcefully unmounted * and the vnode's v_vfsp could have been invalidated * by VFS_UNMOUNT. Hence, we cache v_vfsp and use it * with vfs_rlock_wait/vfs_unlock. * It is safe to use the v_vfsp even it is freed by * VFS_UNMOUNT because vfs_rlock_wait/vfs_unlock * do not dereference v_vfsp. It is just used as a * magic cookie. * One more corner case here is the memory getting * reused for another vfs structure. In this case * lookuppnvp's vfs_rlock_wait will succeed, domount's * vfs_lock will fail and domount will bail out with an * error (EBUSY). */ vfsp = cvp->v_vfsp; /* * This lock is used to synchronize * mounts/unmounts and lookups. * Threads doing mounts/unmounts hold the * writers version vfs_lock_wait(). */ vfs_rlock_wait(vfsp); /* * If this vnode is on a file system that * has been forcibly unmounted, * we can't proceed. Cancel this operation * and return EIO. * * vfs_vnodecovered is NULL if unmounted. * Currently, nfs uses VFS_UNMOUNTED to * check if it's a forced-umount. Keep the * same checking here as well even though it * may not be needed. */ if (((vp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { vfs_unlock(vfsp); VN_RELE(cvp); if (pp) pn_free(pp); return (EIO); } VN_HOLD(vp); vfs_unlock(vfsp); VN_RELE(cvp); cvp = NULL; /* * Crossing mount points. For eg: We are doing * a lookup of ".." for file systems root vnode * mounted here, and VOP_LOOKUP() (with covered vnode) * will be on underlying file systems mount point * vnode. Set retry_with_kcred flag as we might end * up doing VOP_LOOKUP() with kcred if required. */ retry_with_kcred = B_TRUE; goto checkforroot; } } /* * LOOKUP_CHECKREAD is a private flag used by vnodetopath() to indicate * that we need to have read permission on every directory in the entire * path. This is used to ensure that a forward-lookup of a cached value * has the same effect as a reverse-lookup when the cached value cannot * be found. */ if ((flags & LOOKUP_CHECKREAD) && (error = VOP_ACCESS(vp, VREAD, 0, cr, NULL)) != 0) goto bad; /* * Perform a lookup in the current directory. */ error = VOP_LOOKUP(vp, component, &cvp, pnp, lookup_flags, rootvp, cr, NULL, NULL, pp); /* * Retry with kcred - If crossing mount points & error is EACCES. * * If we are crossing mount points here and doing ".." lookup, * VOP_LOOKUP() might fail if the underlying file systems * mount point has no execute permission. In cases like these, * we retry VOP_LOOKUP() by giving as much privilage as possible * by passing kcred credentials. * * In case of hierarchical file systems, passing kcred still may * or may not work. * For eg: UFS FS --> Mount NFS FS --> Again mount UFS on some * directory inside NFS FS. */ if ((error == EACCES) && retry_with_kcred) error = VOP_LOOKUP(vp, component, &cvp, pnp, lookup_flags, rootvp, zone_kcred(), NULL, NULL, pp); if (error) { cvp = NULL; /* * On error, return hard error if * (a) we're not at the end of the pathname yet, or * (b) the caller didn't want the parent directory, or * (c) we failed for some reason other than a missing entry. */ if (pn_pathleft(pnp) || dirvpp == NULL || error != ENOENT) goto bad; if (auditing) { /* directory access */ if (error = audit_savepath(pnp, vp, vp, error, cr)) goto bad_noaudit; } pn_setlast(pnp); /* * We inform the caller that the desired entry must be * a directory by adding a '/' to the component name. */ if (must_be_directory && (error = pn_addslash(pnp)) != 0) goto bad; *dirvpp = vp; if (compvpp != NULL) *compvpp = NULL; if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (0); } /* * Traverse mount points. * XXX why don't we need to hold a read lock here (call vn_vfsrlock)? * What prevents a concurrent update to v_vfsmountedhere? * Possible answer: if mounting, we might not see the mount * if it is concurrently coming into existence, but that's * really not much different from the thread running a bit slower. * If unmounting, we may get into traverse() when we shouldn't, * but traverse() will catch this case for us. * (For this to work, fetching v_vfsmountedhere had better * be atomic!) */ if (vn_mountedvfs(cvp) != NULL) { if ((error = traverse(&cvp)) != 0) goto bad; } /* * If we hit a symbolic link and there is more path to be * translated or this operation does not wish to apply * to a link, then place the contents of the link at the * front of the remaining pathname. */ if (cvp->v_type == VLNK && ((flags & FOLLOW) || pn_pathleft(pnp))) { struct pathname linkpath; if (++nlink > MAXSYMLINKS) { error = ELOOP; goto bad; } pn_alloc(&linkpath); if (error = pn_getsymlink(cvp, &linkpath, cr)) { pn_free(&linkpath); goto bad; } if (auditing) audit_symlink(pnp, &linkpath); if (pn_pathleft(&linkpath) == 0) (void) pn_set(&linkpath, "."); error = pn_insert(pnp, &linkpath, strlen(component)); pn_free(&linkpath); if (error) goto bad; VN_RELE(cvp); cvp = NULL; if (pnp->pn_pathlen == 0) { error = ENOENT; goto bad; } if (pnp->pn_path[0] == '/') { do { pnp->pn_path++; pnp->pn_pathlen--; } while (pnp->pn_path[0] == '/'); VN_RELE(vp); vp = rootvp; VN_HOLD(vp); } if (auditing) audit_anchorpath(pnp, vp == rootvp); if (pn_fixslash(pnp)) { flags |= FOLLOW; must_be_directory = 1; } goto next; } /* * If rpnp is non-NULL, remember the resolved path name therein. * Do not include "." components. Collapse occurrences of * "previous/..", so long as "previous" is not itself "..". * Exhausting rpnp results in error ENAMETOOLONG. */ if (rpnp && strcmp(component, ".") != 0) { size_t len; if (strcmp(component, "..") == 0 && rpnp->pn_pathlen != 0 && !((rpnp->pn_pathlen > 2 && strncmp(rpnp->pn_path+rpnp->pn_pathlen-3, "/..", 3) == 0) || (rpnp->pn_pathlen == 2 && strncmp(rpnp->pn_path, "..", 2) == 0))) { while (rpnp->pn_pathlen && rpnp->pn_path[rpnp->pn_pathlen-1] != '/') rpnp->pn_pathlen--; if (rpnp->pn_pathlen > 1) rpnp->pn_pathlen--; rpnp->pn_path[rpnp->pn_pathlen] = '\0'; } else { if (rpnp->pn_pathlen != 0 && rpnp->pn_path[rpnp->pn_pathlen-1] != '/') rpnp->pn_path[rpnp->pn_pathlen++] = '/'; if (flags & FIGNORECASE) { /* * Return the case-preserved name * within the resolved path. */ error = copystr(pp->pn_buf, rpnp->pn_path + rpnp->pn_pathlen, rpnp->pn_bufsize - rpnp->pn_pathlen, &len); } else { error = copystr(component, rpnp->pn_path + rpnp->pn_pathlen, rpnp->pn_bufsize - rpnp->pn_pathlen, &len); } if (error) /* copystr() returns ENAMETOOLONG */ goto bad; rpnp->pn_pathlen += (len - 1); ASSERT(rpnp->pn_bufsize > rpnp->pn_pathlen); } } /* * If no more components, return last directory (if wanted) and * last component (if wanted). */ if (pn_pathleft(pnp) == 0) { /* * If there was a trailing slash in the pathname, * make sure the last component is a directory. */ if (must_be_directory && cvp->v_type != VDIR) { error = ENOTDIR; goto bad; } if (dirvpp != NULL) { /* * Check that we have the real parent and not * an alias of the last component. */ if (vn_compare(vp, cvp)) { if (auditing) (void) audit_savepath(pnp, cvp, vp, EINVAL, cr); pn_setlast(pnp); VN_RELE(vp); VN_RELE(cvp); if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (EINVAL); } *dirvpp = vp; } else VN_RELE(vp); if (auditing) (void) audit_savepath(pnp, cvp, vp, 0, cr); if (pnp->pn_path == pnp->pn_buf) (void) pn_set(pnp, "."); else pn_setlast(pnp); if (rpnp) { if (VN_CMP(cvp, rootvp)) (void) pn_set(rpnp, "/"); else if (rpnp->pn_pathlen == 0) (void) pn_set(rpnp, "."); } if (compvpp != NULL) *compvpp = cvp; else VN_RELE(cvp); if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (0); } /* * Skip over slashes from end of last component. */ while (pnp->pn_path[0] == '/') { pnp->pn_path++; pnp->pn_pathlen--; } /* * Searched through another level of directory: * release previous directory handle and save new (result * of lookup) as current directory. */ VN_RELE(vp); vp = cvp; cvp = NULL; goto next; bad: if (auditing) /* reached end of path */ (void) audit_savepath(pnp, cvp, vp, error, cr); bad_noaudit: /* * Error. Release vnodes and return. */ if (cvp) VN_RELE(cvp); /* * 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 (error == ESTALE && (VN_CMP(vp, rootvp) || (vp->v_flag & VROOT) || vp == startvp)) error = ENOENT; VN_RELE(vp); if (rootvp != rootdir) VN_RELE(rootvp); if (pp) pn_free(pp); return (error); }
int dogetcwd(char *buf, size_t buflen) { int ret; vnode_t *vp; vnode_t *compvp; refstr_t *cwd, *oldcwd; const char *value; pathname_t rpnp, pnp; proc_t *p = curproc; /* * Check to see if there is a cached version of the cwd. If so, lookup * the cached value and make sure it is the same vnode. */ mutex_enter(&p->p_lock); if ((cwd = PTOU(p)->u_cwd) != NULL) refstr_hold(cwd); vp = PTOU(p)->u_cdir; VN_HOLD(vp); mutex_exit(&p->p_lock); /* * Make sure we have permission to access the current directory. */ if ((ret = VOP_ACCESS(vp, VEXEC, 0, CRED(), NULL)) != 0) { if (cwd != NULL) refstr_rele(cwd); VN_RELE(vp); return (ret); } if (cwd) { value = refstr_value(cwd); if ((ret = pn_get((char *)value, UIO_SYSSPACE, &pnp)) != 0) { refstr_rele(cwd); VN_RELE(vp); return (ret); } pn_alloc(&rpnp); if (lookuppn(&pnp, &rpnp, NO_FOLLOW, NULL, &compvp) == 0) { if (VN_CMP(vp, compvp) && strcmp(value, rpnp.pn_path) == 0) { VN_RELE(compvp); VN_RELE(vp); pn_free(&pnp); pn_free(&rpnp); if (strlen(value) + 1 > buflen) { refstr_rele(cwd); return (ENAMETOOLONG); } bcopy(value, buf, strlen(value) + 1); refstr_rele(cwd); return (0); } VN_RELE(compvp); } pn_free(&rpnp); pn_free(&pnp); refstr_rele(cwd); } ret = vnodetopath_common(NULL, vp, buf, buflen, CRED(), LOOKUP_CHECKREAD); VN_RELE(vp); /* * Store the new cwd and replace the existing cached copy. */ if (ret == 0) cwd = refstr_alloc(buf); else cwd = NULL; mutex_enter(&p->p_lock); oldcwd = PTOU(p)->u_cwd; PTOU(p)->u_cwd = cwd; mutex_exit(&p->p_lock); if (oldcwd) refstr_rele(oldcwd); return (ret); }
/* * 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); }
/* * smb_vop_lookup * * dvp: directory vnode (in) * name: name of file to be looked up (in) * vpp: looked-up vnode (out) * od_name: on-disk name of file (out). * This parameter is optional. If a pointer is passed in, it * must be allocated with MAXNAMELEN bytes * rootvp: vnode of the tree root (in) * This parameter is always passed in non-NULL except at the time * of share set up. * direntflags: dirent flags returned from VOP_LOOKUP */ int smb_vop_lookup( vnode_t *dvp, char *name, vnode_t **vpp, char *od_name, int flags, int *direntflags, vnode_t *rootvp, cred_t *cr) { int error = 0; int option_flags = 0; pathname_t rpn; char *np = name; char namebuf[MAXNAMELEN]; if (*name == '\0') return (EINVAL); ASSERT(vpp); *vpp = NULL; *direntflags = 0; if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) { if (rootvp && (dvp == rootvp)) { VN_HOLD(dvp); *vpp = dvp; return (0); } if (dvp->v_flag & VROOT) { vfs_t *vfsp; vnode_t *cvp = dvp; /* * Set dvp and check for races with forced unmount * (see lookuppnvp()) */ vfsp = cvp->v_vfsp; vfs_rlock_wait(vfsp); if (((dvp = cvp->v_vfsp->vfs_vnodecovered) == NULL) || (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) { vfs_unlock(vfsp); return (EIO); } vfs_unlock(vfsp); } } if (flags & SMB_IGNORE_CASE) option_flags = FIGNORECASE; if (flags & SMB_CATIA) np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); pn_alloc(&rpn); error = VOP_LOOKUP(dvp, np, vpp, NULL, option_flags, NULL, cr, &smb_ct, direntflags, &rpn); if ((error == 0) && od_name) { bzero(od_name, MAXNAMELEN); np = (option_flags == FIGNORECASE) ? rpn.pn_buf : name; if (flags & SMB_CATIA) smb_vop_catia_v4tov5(np, od_name, MAXNAMELEN); else (void) strlcpy(od_name, np, MAXNAMELEN); } pn_free(&rpn); return (error); }