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
pn_alloc(struct pathname * pnp)737c478bd9Sstevel@tonic-gate pn_alloc(struct pathname *pnp)
747c478bd9Sstevel@tonic-gate {
75b24e356bSPeer Dampmann pn_alloc_sz(pnp, MAXPATHLEN);
76b24e356bSPeer Dampmann }
77b24e356bSPeer Dampmann void
pn_alloc_sz(struct pathname * pnp,size_t sz)78b24e356bSPeer Dampmann pn_alloc_sz(struct pathname *pnp, size_t sz)
79b24e356bSPeer Dampmann {
80b24e356bSPeer Dampmann pnp->pn_path = pnp->pn_buf = kmem_alloc(sz, KM_SLEEP);
817c478bd9Sstevel@tonic-gate pnp->pn_pathlen = 0;
82b24e356bSPeer 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
pn_free(struct pathname * pnp)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
pn_get_buf(const char * str,enum uio_seg seg,struct pathname * pnp,void * buf,size_t bufsize)104*5f61829aSRobert Mustacchi pn_get_buf(const 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
pn_get(const char * str,enum uio_seg seg,struct pathname * pnp)125*5f61829aSRobert Mustacchi pn_get(const 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
pn_set(struct pathname * pnp,const char * path)143*5f61829aSRobert Mustacchi pn_set(struct pathname *pnp, const 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
pn_insert(struct pathname * pnp,struct pathname * sympnp,size_t complen)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
pn_getsymlink(vnode_t * vp,struct pathname * pnp,cred_t * crp)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
pn_getcomponent(struct pathname * pnp,char * component)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
pn_skipslash(struct pathname * pnp)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
pn_setlast(struct pathname * pnp)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
pn_fixslash(struct pathname * pnp)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
pn_addslash(struct pathname * pnp)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