xref: /illumos-gate/usr/src/uts/common/fs/pathname.c (revision 5f61829a)
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	*/
27*5f61829aSRobert Mustacchi /*	  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,
105*5f61829aSRobert Mustacchi     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