17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22da6c28aaSamw * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 277c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 317c478bd9Sstevel@tonic-gate * The Regents of the University of California 327c478bd9Sstevel@tonic-gate * All Rights Reserved 337c478bd9Sstevel@tonic-gate * 347c478bd9Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 357c478bd9Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 367c478bd9Sstevel@tonic-gate * contributors. 377c478bd9Sstevel@tonic-gate */ 387c478bd9Sstevel@tonic-gate 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate #include <sys/types.h> 417c478bd9Sstevel@tonic-gate #include <sys/param.h> 427c478bd9Sstevel@tonic-gate #include <sys/systm.h> 437c478bd9Sstevel@tonic-gate #include <sys/uio.h> 447c478bd9Sstevel@tonic-gate #include <sys/errno.h> 457c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 467c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 477c478bd9Sstevel@tonic-gate #include <sys/cred.h> 487c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 497c478bd9Sstevel@tonic-gate #include <sys/debug.h> 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate /* 527c478bd9Sstevel@tonic-gate * Pathname utilities. 537c478bd9Sstevel@tonic-gate * 547c478bd9Sstevel@tonic-gate * In translating file names we copy each argument file 557c478bd9Sstevel@tonic-gate * name into a pathname structure where we operate on it. 567c478bd9Sstevel@tonic-gate * Each pathname structure can hold "pn_bufsize" characters 577c478bd9Sstevel@tonic-gate * including a terminating null, and operations here support 587c478bd9Sstevel@tonic-gate * allocating and freeing pathname structures, fetching 597c478bd9Sstevel@tonic-gate * strings from user space, getting the next character from 607c478bd9Sstevel@tonic-gate * a pathname, combining two pathnames (used in symbolic 617c478bd9Sstevel@tonic-gate * link processing), and peeling off the first component 627c478bd9Sstevel@tonic-gate * of a pathname. 637c478bd9Sstevel@tonic-gate */ 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate /* 667c478bd9Sstevel@tonic-gate * Allocate contents of pathname structure. Structure is typically 677c478bd9Sstevel@tonic-gate * an automatic variable in calling routine for convenience. 687c478bd9Sstevel@tonic-gate * 697c478bd9Sstevel@tonic-gate * May sleep in the call to kmem_alloc() and so must not be called 707c478bd9Sstevel@tonic-gate * from interrupt level. 717c478bd9Sstevel@tonic-gate */ 727c478bd9Sstevel@tonic-gate void 737c478bd9Sstevel@tonic-gate pn_alloc(struct pathname *pnp) 747c478bd9Sstevel@tonic-gate { 75*b24e356bSPeer Dampmann pn_alloc_sz(pnp, MAXPATHLEN); 76*b24e356bSPeer Dampmann } 77*b24e356bSPeer Dampmann void 78*b24e356bSPeer Dampmann pn_alloc_sz(struct pathname *pnp, size_t sz) 79*b24e356bSPeer Dampmann { 80*b24e356bSPeer Dampmann pnp->pn_path = pnp->pn_buf = kmem_alloc(sz, KM_SLEEP); 817c478bd9Sstevel@tonic-gate pnp->pn_pathlen = 0; 82*b24e356bSPeer Dampmann pnp->pn_bufsize = sz; 837c478bd9Sstevel@tonic-gate } 847c478bd9Sstevel@tonic-gate 857c478bd9Sstevel@tonic-gate /* 867c478bd9Sstevel@tonic-gate * Free pathname resources. 877c478bd9Sstevel@tonic-gate */ 887c478bd9Sstevel@tonic-gate void 897c478bd9Sstevel@tonic-gate pn_free(struct pathname *pnp) 907c478bd9Sstevel@tonic-gate { 919985ed6fSdmick /* pn_bufsize is usually MAXPATHLEN, but may not be */ 929985ed6fSdmick kmem_free(pnp->pn_buf, pnp->pn_bufsize); 937c478bd9Sstevel@tonic-gate pnp->pn_path = pnp->pn_buf = NULL; 947c478bd9Sstevel@tonic-gate pnp->pn_pathlen = pnp->pn_bufsize = 0; 957c478bd9Sstevel@tonic-gate } 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate /* 987c478bd9Sstevel@tonic-gate * Pull a path name from user or kernel space. 997c478bd9Sstevel@tonic-gate * Called from pn_get() after allocation of a MAXPATHLEN buffer. 1007c478bd9Sstevel@tonic-gate * Also called directly with a TYPICALMAXPATHLEN-size buffer 1017c478bd9Sstevel@tonic-gate * on the stack as a local optimization. 1027c478bd9Sstevel@tonic-gate */ 1037c478bd9Sstevel@tonic-gate int 1047c478bd9Sstevel@tonic-gate pn_get_buf(char *str, enum uio_seg seg, struct pathname *pnp, 1057c478bd9Sstevel@tonic-gate void *buf, size_t bufsize) 1067c478bd9Sstevel@tonic-gate { 1077c478bd9Sstevel@tonic-gate int error; 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate pnp->pn_path = pnp->pn_buf = buf; 1107c478bd9Sstevel@tonic-gate pnp->pn_bufsize = bufsize; 1117c478bd9Sstevel@tonic-gate if (seg == UIO_USERSPACE) 1127c478bd9Sstevel@tonic-gate error = copyinstr(str, pnp->pn_path, bufsize, &pnp->pn_pathlen); 1137c478bd9Sstevel@tonic-gate else 1147c478bd9Sstevel@tonic-gate error = copystr(str, pnp->pn_path, bufsize, &pnp->pn_pathlen); 1157c478bd9Sstevel@tonic-gate if (error) 1167c478bd9Sstevel@tonic-gate return (error); 1177c478bd9Sstevel@tonic-gate pnp->pn_pathlen--; /* don't count null byte */ 1187c478bd9Sstevel@tonic-gate return (0); 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate /* 1227c478bd9Sstevel@tonic-gate * Pull a path name from user or kernel space. 1237c478bd9Sstevel@tonic-gate */ 1247c478bd9Sstevel@tonic-gate int 1257c478bd9Sstevel@tonic-gate pn_get(char *str, enum uio_seg seg, struct pathname *pnp) 1267c478bd9Sstevel@tonic-gate { 1277c478bd9Sstevel@tonic-gate int error; 1287c478bd9Sstevel@tonic-gate void *buf; 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate buf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 1317c478bd9Sstevel@tonic-gate if ((error = pn_get_buf(str, seg, pnp, buf, MAXPATHLEN)) != 0) 1327c478bd9Sstevel@tonic-gate pn_free(pnp); 1337c478bd9Sstevel@tonic-gate return (error); 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /* 1377c478bd9Sstevel@tonic-gate * Set path name to argument string. Storage has already been allocated 1387c478bd9Sstevel@tonic-gate * and pn_buf points to it. 1397c478bd9Sstevel@tonic-gate * 1407c478bd9Sstevel@tonic-gate * On error, all fields except pn_buf will be undefined. 1417c478bd9Sstevel@tonic-gate */ 1427c478bd9Sstevel@tonic-gate int 1437c478bd9Sstevel@tonic-gate pn_set(struct pathname *pnp, char *path) 1447c478bd9Sstevel@tonic-gate { 1457c478bd9Sstevel@tonic-gate int error; 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate pnp->pn_path = pnp->pn_buf; 1487c478bd9Sstevel@tonic-gate error = copystr(path, pnp->pn_path, pnp->pn_bufsize, &pnp->pn_pathlen); 1497c478bd9Sstevel@tonic-gate pnp->pn_pathlen--; /* don't count null byte */ 1507c478bd9Sstevel@tonic-gate return (error); 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate /* 1547c478bd9Sstevel@tonic-gate * Combine two argument path names by putting the second argument 1557c478bd9Sstevel@tonic-gate * before the first in the first's buffer. This isn't very general; 1567c478bd9Sstevel@tonic-gate * it is designed specifically for symbolic link processing. 1577c478bd9Sstevel@tonic-gate * This function copies the symlink in-place in the pathname. This is to 1587c478bd9Sstevel@tonic-gate * ensure that vnode path caching remains correct. At the point where this is 1597c478bd9Sstevel@tonic-gate * called (from lookuppnvp), we have called pn_getcomponent(), found it is a 1607c478bd9Sstevel@tonic-gate * symlink, and are now replacing the contents. The complen parameter indicates 1617c478bd9Sstevel@tonic-gate * how much of the pathname to replace. If the symlink is an absolute path, 1627c478bd9Sstevel@tonic-gate * then we overwrite the entire contents of the pathname. 1637c478bd9Sstevel@tonic-gate */ 1647c478bd9Sstevel@tonic-gate int 1657c478bd9Sstevel@tonic-gate pn_insert(struct pathname *pnp, struct pathname *sympnp, size_t complen) 1667c478bd9Sstevel@tonic-gate { 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate if (*sympnp->pn_path == '/') { 1697c478bd9Sstevel@tonic-gate /* 1707c478bd9Sstevel@tonic-gate * Full path, replace everything 1717c478bd9Sstevel@tonic-gate */ 1727c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen + sympnp->pn_pathlen >= pnp->pn_bufsize) 1737c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 1747c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen != 0) 1757c478bd9Sstevel@tonic-gate ovbcopy(pnp->pn_path, pnp->pn_buf + sympnp->pn_pathlen, 1767c478bd9Sstevel@tonic-gate pnp->pn_pathlen); 1777c478bd9Sstevel@tonic-gate bcopy(sympnp->pn_path, pnp->pn_buf, sympnp->pn_pathlen); 1787c478bd9Sstevel@tonic-gate pnp->pn_pathlen += sympnp->pn_pathlen; 1797c478bd9Sstevel@tonic-gate pnp->pn_buf[pnp->pn_pathlen] = '\0'; 1807c478bd9Sstevel@tonic-gate pnp->pn_path = pnp->pn_buf; 1817c478bd9Sstevel@tonic-gate } else { 1827c478bd9Sstevel@tonic-gate /* 1837c478bd9Sstevel@tonic-gate * Partial path, replace only last component 1847c478bd9Sstevel@tonic-gate */ 1857c478bd9Sstevel@tonic-gate if ((pnp->pn_path - pnp->pn_buf) - complen + 1867c478bd9Sstevel@tonic-gate pnp->pn_pathlen + sympnp->pn_pathlen >= pnp->pn_bufsize) 1877c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen != 0) 1907c478bd9Sstevel@tonic-gate ovbcopy(pnp->pn_path, pnp->pn_path - complen + 1917c478bd9Sstevel@tonic-gate sympnp->pn_pathlen, pnp->pn_pathlen + 1); 1927c478bd9Sstevel@tonic-gate pnp->pn_path -= complen; 1937c478bd9Sstevel@tonic-gate bcopy(sympnp->pn_path, pnp->pn_path, sympnp->pn_pathlen); 1947c478bd9Sstevel@tonic-gate pnp->pn_pathlen += sympnp->pn_pathlen; 1957c478bd9Sstevel@tonic-gate } 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate return (0); 1987c478bd9Sstevel@tonic-gate } 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate int 2017c478bd9Sstevel@tonic-gate pn_getsymlink(vnode_t *vp, struct pathname *pnp, cred_t *crp) 2027c478bd9Sstevel@tonic-gate { 2037c478bd9Sstevel@tonic-gate struct iovec aiov; 2047c478bd9Sstevel@tonic-gate struct uio auio; 2057c478bd9Sstevel@tonic-gate int error; 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate aiov.iov_base = pnp->pn_path = pnp->pn_buf; 2087c478bd9Sstevel@tonic-gate aiov.iov_len = pnp->pn_bufsize; 2097c478bd9Sstevel@tonic-gate auio.uio_iov = &aiov; 2107c478bd9Sstevel@tonic-gate auio.uio_iovcnt = 1; 2117c478bd9Sstevel@tonic-gate auio.uio_loffset = 0; 2127c478bd9Sstevel@tonic-gate auio.uio_segflg = UIO_SYSSPACE; 2137c478bd9Sstevel@tonic-gate auio.uio_extflg = UIO_COPY_CACHED; 2147c478bd9Sstevel@tonic-gate auio.uio_resid = pnp->pn_bufsize; 215da6c28aaSamw if ((error = VOP_READLINK(vp, &auio, crp, NULL)) == 0) { 2167c478bd9Sstevel@tonic-gate pnp->pn_pathlen = pnp->pn_bufsize - auio.uio_resid; 2177c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen == pnp->pn_bufsize) 2187c478bd9Sstevel@tonic-gate error = ENAMETOOLONG; 2197c478bd9Sstevel@tonic-gate else 2207c478bd9Sstevel@tonic-gate pnp->pn_path[pnp->pn_pathlen] = '\0'; 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate return (error); 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate /* 2267c478bd9Sstevel@tonic-gate * Get next component from a path name and leave in 2277c478bd9Sstevel@tonic-gate * buffer "component" which should have room for 2287c478bd9Sstevel@tonic-gate * MAXNAMELEN bytes (including a null terminator character). 2297c478bd9Sstevel@tonic-gate */ 2307c478bd9Sstevel@tonic-gate int 2317c478bd9Sstevel@tonic-gate pn_getcomponent(struct pathname *pnp, char *component) 2327c478bd9Sstevel@tonic-gate { 2337c478bd9Sstevel@tonic-gate char c, *cp, *path, saved; 2347c478bd9Sstevel@tonic-gate size_t pathlen; 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate path = pnp->pn_path; 2377c478bd9Sstevel@tonic-gate pathlen = pnp->pn_pathlen; 2387c478bd9Sstevel@tonic-gate if (pathlen >= MAXNAMELEN) { 2397c478bd9Sstevel@tonic-gate saved = path[MAXNAMELEN]; 2407c478bd9Sstevel@tonic-gate path[MAXNAMELEN] = '/'; /* guarantees loop termination */ 2417c478bd9Sstevel@tonic-gate for (cp = path; (c = *cp) != '/'; cp++) 2427c478bd9Sstevel@tonic-gate *component++ = c; 2437c478bd9Sstevel@tonic-gate path[MAXNAMELEN] = saved; 2447c478bd9Sstevel@tonic-gate if (cp - path == MAXNAMELEN) 2457c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 2467c478bd9Sstevel@tonic-gate } else { 2477c478bd9Sstevel@tonic-gate path[pathlen] = '/'; /* guarantees loop termination */ 2487c478bd9Sstevel@tonic-gate for (cp = path; (c = *cp) != '/'; cp++) 2497c478bd9Sstevel@tonic-gate *component++ = c; 2507c478bd9Sstevel@tonic-gate path[pathlen] = '\0'; 2517c478bd9Sstevel@tonic-gate } 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate pnp->pn_path = cp; 2547c478bd9Sstevel@tonic-gate pnp->pn_pathlen = pathlen - (cp - path); 2557c478bd9Sstevel@tonic-gate *component = '\0'; 2567c478bd9Sstevel@tonic-gate return (0); 2577c478bd9Sstevel@tonic-gate } 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate /* 2607c478bd9Sstevel@tonic-gate * Skip over consecutive slashes in the path name. 2617c478bd9Sstevel@tonic-gate */ 2627c478bd9Sstevel@tonic-gate void 2637c478bd9Sstevel@tonic-gate pn_skipslash(struct pathname *pnp) 2647c478bd9Sstevel@tonic-gate { 2657c478bd9Sstevel@tonic-gate while (pnp->pn_pathlen > 0 && *pnp->pn_path == '/') { 2667c478bd9Sstevel@tonic-gate pnp->pn_path++; 2677c478bd9Sstevel@tonic-gate pnp->pn_pathlen--; 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate } 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate /* 2727c478bd9Sstevel@tonic-gate * Sets pn_path to the last component in the pathname, updating 2737c478bd9Sstevel@tonic-gate * pn_pathlen. If pathname is empty, or degenerate, leaves pn_path 2747c478bd9Sstevel@tonic-gate * pointing at NULL char. The pathname is explicitly null-terminated 2757c478bd9Sstevel@tonic-gate * so that any trailing slashes are effectively removed. 2767c478bd9Sstevel@tonic-gate */ 2777c478bd9Sstevel@tonic-gate void 2787c478bd9Sstevel@tonic-gate pn_setlast(struct pathname *pnp) 2797c478bd9Sstevel@tonic-gate { 2807c478bd9Sstevel@tonic-gate char *buf = pnp->pn_buf; 2817c478bd9Sstevel@tonic-gate char *path = pnp->pn_path + pnp->pn_pathlen - 1; 2827c478bd9Sstevel@tonic-gate char *endpath; 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate while (path > buf && *path == '/') 2857c478bd9Sstevel@tonic-gate --path; 2867c478bd9Sstevel@tonic-gate endpath = path + 1; 2877c478bd9Sstevel@tonic-gate while (path > buf && *path != '/') 2887c478bd9Sstevel@tonic-gate --path; 2897c478bd9Sstevel@tonic-gate if (*path == '/') 2907c478bd9Sstevel@tonic-gate path++; 2917c478bd9Sstevel@tonic-gate *endpath = '\0'; 2927c478bd9Sstevel@tonic-gate pnp->pn_path = path; 2937c478bd9Sstevel@tonic-gate pnp->pn_pathlen = endpath - path; 2947c478bd9Sstevel@tonic-gate } 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate /* 2977c478bd9Sstevel@tonic-gate * Eliminate any trailing slashes in the pathname. 2987c478bd9Sstevel@tonic-gate * Return non-zero iff there were any trailing slashes. 2997c478bd9Sstevel@tonic-gate */ 3007c478bd9Sstevel@tonic-gate int 3017c478bd9Sstevel@tonic-gate pn_fixslash(struct pathname *pnp) 3027c478bd9Sstevel@tonic-gate { 3037c478bd9Sstevel@tonic-gate char *start = pnp->pn_path; 3047c478bd9Sstevel@tonic-gate char *end = start + pnp->pn_pathlen; 3057c478bd9Sstevel@tonic-gate 3067c478bd9Sstevel@tonic-gate while (end > start && *(end - 1) == '/') 3077c478bd9Sstevel@tonic-gate end--; 3087c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen == end - start) 3097c478bd9Sstevel@tonic-gate return (0); 3107c478bd9Sstevel@tonic-gate *end = '\0'; 3117c478bd9Sstevel@tonic-gate pnp->pn_pathlen = end - start; 3127c478bd9Sstevel@tonic-gate return (1); 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate /* 3167c478bd9Sstevel@tonic-gate * Add a slash to the end of the pathname, if it will fit. 3177c478bd9Sstevel@tonic-gate * Return ENAMETOOLONG if it won't. 3187c478bd9Sstevel@tonic-gate */ 3197c478bd9Sstevel@tonic-gate int 3207c478bd9Sstevel@tonic-gate pn_addslash(struct pathname *pnp) 3217c478bd9Sstevel@tonic-gate { 3227c478bd9Sstevel@tonic-gate if (pnp->pn_path + pnp->pn_pathlen + 1 >= 3237c478bd9Sstevel@tonic-gate pnp->pn_buf + pnp->pn_bufsize) { 3247c478bd9Sstevel@tonic-gate if (pnp->pn_pathlen + 1 >= pnp->pn_bufsize) /* no room */ 3257c478bd9Sstevel@tonic-gate return (ENAMETOOLONG); 3267c478bd9Sstevel@tonic-gate /* 3277c478bd9Sstevel@tonic-gate * Move the component to the start of the buffer 3287c478bd9Sstevel@tonic-gate * so we have room to add the trailing slash. 3297c478bd9Sstevel@tonic-gate */ 3307c478bd9Sstevel@tonic-gate ovbcopy(pnp->pn_path, pnp->pn_buf, pnp->pn_pathlen); 3317c478bd9Sstevel@tonic-gate pnp->pn_path = pnp->pn_buf; 3327c478bd9Sstevel@tonic-gate } 3337c478bd9Sstevel@tonic-gate pnp->pn_path[pnp->pn_pathlen++] = '/'; 3347c478bd9Sstevel@tonic-gate pnp->pn_path[pnp->pn_pathlen] = '\0'; 3357c478bd9Sstevel@tonic-gate return (0); 3367c478bd9Sstevel@tonic-gate } 337