xref: /titanic_53/usr/src/uts/common/fs/pathname.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate /*
31*7c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
32*7c478bd9Sstevel@tonic-gate  * The Regents of the University of California
33*7c478bd9Sstevel@tonic-gate  * All Rights Reserved
34*7c478bd9Sstevel@tonic-gate  *
35*7c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
36*7c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
37*7c478bd9Sstevel@tonic-gate  * contributors.
38*7c478bd9Sstevel@tonic-gate  */
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/uio.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
53*7c478bd9Sstevel@tonic-gate 
54*7c478bd9Sstevel@tonic-gate /*
55*7c478bd9Sstevel@tonic-gate  * Pathname utilities.
56*7c478bd9Sstevel@tonic-gate  *
57*7c478bd9Sstevel@tonic-gate  * In translating file names we copy each argument file
58*7c478bd9Sstevel@tonic-gate  * name into a pathname structure where we operate on it.
59*7c478bd9Sstevel@tonic-gate  * Each pathname structure can hold "pn_bufsize" characters
60*7c478bd9Sstevel@tonic-gate  * including a terminating null, and operations here support
61*7c478bd9Sstevel@tonic-gate  * allocating and freeing pathname structures, fetching
62*7c478bd9Sstevel@tonic-gate  * strings from user space, getting the next character from
63*7c478bd9Sstevel@tonic-gate  * a pathname, combining two pathnames (used in symbolic
64*7c478bd9Sstevel@tonic-gate  * link processing), and peeling off the first component
65*7c478bd9Sstevel@tonic-gate  * of a pathname.
66*7c478bd9Sstevel@tonic-gate  */
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate /*
69*7c478bd9Sstevel@tonic-gate  * Allocate contents of pathname structure.  Structure is typically
70*7c478bd9Sstevel@tonic-gate  * an automatic variable in calling routine for convenience.
71*7c478bd9Sstevel@tonic-gate  *
72*7c478bd9Sstevel@tonic-gate  * May sleep in the call to kmem_alloc() and so must not be called
73*7c478bd9Sstevel@tonic-gate  * from interrupt level.
74*7c478bd9Sstevel@tonic-gate  */
75*7c478bd9Sstevel@tonic-gate void
76*7c478bd9Sstevel@tonic-gate pn_alloc(struct pathname *pnp)
77*7c478bd9Sstevel@tonic-gate {
78*7c478bd9Sstevel@tonic-gate 	pnp->pn_path = pnp->pn_buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
79*7c478bd9Sstevel@tonic-gate 	pnp->pn_pathlen = 0;
80*7c478bd9Sstevel@tonic-gate 	pnp->pn_bufsize = MAXPATHLEN;
81*7c478bd9Sstevel@tonic-gate }
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate /*
84*7c478bd9Sstevel@tonic-gate  * Free pathname resources.
85*7c478bd9Sstevel@tonic-gate  */
86*7c478bd9Sstevel@tonic-gate void
87*7c478bd9Sstevel@tonic-gate pn_free(struct pathname *pnp)
88*7c478bd9Sstevel@tonic-gate {
89*7c478bd9Sstevel@tonic-gate 	kmem_free(pnp->pn_buf, MAXPATHLEN);
90*7c478bd9Sstevel@tonic-gate 	pnp->pn_path = pnp->pn_buf = NULL;
91*7c478bd9Sstevel@tonic-gate 	pnp->pn_pathlen = pnp->pn_bufsize = 0;
92*7c478bd9Sstevel@tonic-gate }
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate /*
95*7c478bd9Sstevel@tonic-gate  * Pull a path name from user or kernel space.
96*7c478bd9Sstevel@tonic-gate  * Called from pn_get() after allocation of a MAXPATHLEN buffer.
97*7c478bd9Sstevel@tonic-gate  * Also called directly with a TYPICALMAXPATHLEN-size buffer
98*7c478bd9Sstevel@tonic-gate  * on the stack as a local optimization.
99*7c478bd9Sstevel@tonic-gate  */
100*7c478bd9Sstevel@tonic-gate int
101*7c478bd9Sstevel@tonic-gate pn_get_buf(char *str, enum uio_seg seg, struct pathname *pnp,
102*7c478bd9Sstevel@tonic-gate 	void *buf, size_t bufsize)
103*7c478bd9Sstevel@tonic-gate {
104*7c478bd9Sstevel@tonic-gate 	int error;
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate 	pnp->pn_path = pnp->pn_buf = buf;
107*7c478bd9Sstevel@tonic-gate 	pnp->pn_bufsize = bufsize;
108*7c478bd9Sstevel@tonic-gate 	if (seg == UIO_USERSPACE)
109*7c478bd9Sstevel@tonic-gate 		error = copyinstr(str, pnp->pn_path, bufsize, &pnp->pn_pathlen);
110*7c478bd9Sstevel@tonic-gate 	else
111*7c478bd9Sstevel@tonic-gate 		error = copystr(str, pnp->pn_path, bufsize, &pnp->pn_pathlen);
112*7c478bd9Sstevel@tonic-gate 	if (error)
113*7c478bd9Sstevel@tonic-gate 		return (error);
114*7c478bd9Sstevel@tonic-gate 	pnp->pn_pathlen--;		/* don't count null byte */
115*7c478bd9Sstevel@tonic-gate 	return (0);
116*7c478bd9Sstevel@tonic-gate }
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate /*
119*7c478bd9Sstevel@tonic-gate  * Pull a path name from user or kernel space.
120*7c478bd9Sstevel@tonic-gate  */
121*7c478bd9Sstevel@tonic-gate int
122*7c478bd9Sstevel@tonic-gate pn_get(char *str, enum uio_seg seg, struct pathname *pnp)
123*7c478bd9Sstevel@tonic-gate {
124*7c478bd9Sstevel@tonic-gate 	int error;
125*7c478bd9Sstevel@tonic-gate 	void *buf;
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate 	buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
128*7c478bd9Sstevel@tonic-gate 	if ((error = pn_get_buf(str, seg, pnp, buf, MAXPATHLEN)) != 0)
129*7c478bd9Sstevel@tonic-gate 		pn_free(pnp);
130*7c478bd9Sstevel@tonic-gate 	return (error);
131*7c478bd9Sstevel@tonic-gate }
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate /*
134*7c478bd9Sstevel@tonic-gate  * Set path name to argument string.  Storage has already been allocated
135*7c478bd9Sstevel@tonic-gate  * and pn_buf points to it.
136*7c478bd9Sstevel@tonic-gate  *
137*7c478bd9Sstevel@tonic-gate  * On error, all fields except pn_buf will be undefined.
138*7c478bd9Sstevel@tonic-gate  */
139*7c478bd9Sstevel@tonic-gate int
140*7c478bd9Sstevel@tonic-gate pn_set(struct pathname *pnp, char *path)
141*7c478bd9Sstevel@tonic-gate {
142*7c478bd9Sstevel@tonic-gate 	int error;
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 	pnp->pn_path = pnp->pn_buf;
145*7c478bd9Sstevel@tonic-gate 	error = copystr(path, pnp->pn_path, pnp->pn_bufsize, &pnp->pn_pathlen);
146*7c478bd9Sstevel@tonic-gate 	pnp->pn_pathlen--;		/* don't count null byte */
147*7c478bd9Sstevel@tonic-gate 	return (error);
148*7c478bd9Sstevel@tonic-gate }
149*7c478bd9Sstevel@tonic-gate 
150*7c478bd9Sstevel@tonic-gate /*
151*7c478bd9Sstevel@tonic-gate  * Combine two argument path names by putting the second argument
152*7c478bd9Sstevel@tonic-gate  * before the first in the first's buffer.  This isn't very general;
153*7c478bd9Sstevel@tonic-gate  * it is designed specifically for symbolic link processing.
154*7c478bd9Sstevel@tonic-gate  * This function copies the symlink in-place in the pathname.  This is to
155*7c478bd9Sstevel@tonic-gate  * ensure that vnode path caching remains correct.  At the point where this is
156*7c478bd9Sstevel@tonic-gate  * called (from lookuppnvp), we have called pn_getcomponent(), found it is a
157*7c478bd9Sstevel@tonic-gate  * symlink, and are now replacing the contents.  The complen parameter indicates
158*7c478bd9Sstevel@tonic-gate  * how much of the pathname to replace.  If the symlink is an absolute path,
159*7c478bd9Sstevel@tonic-gate  * then we overwrite the entire contents of the pathname.
160*7c478bd9Sstevel@tonic-gate  */
161*7c478bd9Sstevel@tonic-gate int
162*7c478bd9Sstevel@tonic-gate pn_insert(struct pathname *pnp, struct pathname *sympnp, size_t complen)
163*7c478bd9Sstevel@tonic-gate {
164*7c478bd9Sstevel@tonic-gate 
165*7c478bd9Sstevel@tonic-gate 	if (*sympnp->pn_path == '/') {
166*7c478bd9Sstevel@tonic-gate 		/*
167*7c478bd9Sstevel@tonic-gate 		 * Full path, replace everything
168*7c478bd9Sstevel@tonic-gate 		 */
169*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_pathlen + sympnp->pn_pathlen >= pnp->pn_bufsize)
170*7c478bd9Sstevel@tonic-gate 			return (ENAMETOOLONG);
171*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_pathlen != 0)
172*7c478bd9Sstevel@tonic-gate 			ovbcopy(pnp->pn_path, pnp->pn_buf + sympnp->pn_pathlen,
173*7c478bd9Sstevel@tonic-gate 			    pnp->pn_pathlen);
174*7c478bd9Sstevel@tonic-gate 		bcopy(sympnp->pn_path, pnp->pn_buf, sympnp->pn_pathlen);
175*7c478bd9Sstevel@tonic-gate 		pnp->pn_pathlen += sympnp->pn_pathlen;
176*7c478bd9Sstevel@tonic-gate 		pnp->pn_buf[pnp->pn_pathlen] = '\0';
177*7c478bd9Sstevel@tonic-gate 		pnp->pn_path = pnp->pn_buf;
178*7c478bd9Sstevel@tonic-gate 	} else {
179*7c478bd9Sstevel@tonic-gate 		/*
180*7c478bd9Sstevel@tonic-gate 		 * Partial path, replace only last component
181*7c478bd9Sstevel@tonic-gate 		 */
182*7c478bd9Sstevel@tonic-gate 		if ((pnp->pn_path - pnp->pn_buf) - complen +
183*7c478bd9Sstevel@tonic-gate 		    pnp->pn_pathlen + sympnp->pn_pathlen >= pnp->pn_bufsize)
184*7c478bd9Sstevel@tonic-gate 			return (ENAMETOOLONG);
185*7c478bd9Sstevel@tonic-gate 
186*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_pathlen != 0)
187*7c478bd9Sstevel@tonic-gate 			ovbcopy(pnp->pn_path, pnp->pn_path - complen +
188*7c478bd9Sstevel@tonic-gate 			    sympnp->pn_pathlen, pnp->pn_pathlen + 1);
189*7c478bd9Sstevel@tonic-gate 		pnp->pn_path -= complen;
190*7c478bd9Sstevel@tonic-gate 		bcopy(sympnp->pn_path, pnp->pn_path, sympnp->pn_pathlen);
191*7c478bd9Sstevel@tonic-gate 		pnp->pn_pathlen += sympnp->pn_pathlen;
192*7c478bd9Sstevel@tonic-gate 	}
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate 	return (0);
195*7c478bd9Sstevel@tonic-gate }
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate int
198*7c478bd9Sstevel@tonic-gate pn_getsymlink(vnode_t *vp, struct pathname *pnp, cred_t *crp)
199*7c478bd9Sstevel@tonic-gate {
200*7c478bd9Sstevel@tonic-gate 	struct iovec aiov;
201*7c478bd9Sstevel@tonic-gate 	struct uio auio;
202*7c478bd9Sstevel@tonic-gate 	int error;
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate 	aiov.iov_base = pnp->pn_path = pnp->pn_buf;
205*7c478bd9Sstevel@tonic-gate 	aiov.iov_len = pnp->pn_bufsize;
206*7c478bd9Sstevel@tonic-gate 	auio.uio_iov = &aiov;
207*7c478bd9Sstevel@tonic-gate 	auio.uio_iovcnt = 1;
208*7c478bd9Sstevel@tonic-gate 	auio.uio_loffset = 0;
209*7c478bd9Sstevel@tonic-gate 	auio.uio_segflg = UIO_SYSSPACE;
210*7c478bd9Sstevel@tonic-gate 	auio.uio_extflg = UIO_COPY_CACHED;
211*7c478bd9Sstevel@tonic-gate 	auio.uio_resid = pnp->pn_bufsize;
212*7c478bd9Sstevel@tonic-gate 	if ((error = VOP_READLINK(vp, &auio, crp)) == 0) {
213*7c478bd9Sstevel@tonic-gate 		pnp->pn_pathlen = pnp->pn_bufsize - auio.uio_resid;
214*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_pathlen == pnp->pn_bufsize)
215*7c478bd9Sstevel@tonic-gate 			error = ENAMETOOLONG;
216*7c478bd9Sstevel@tonic-gate 		else
217*7c478bd9Sstevel@tonic-gate 			pnp->pn_path[pnp->pn_pathlen] = '\0';
218*7c478bd9Sstevel@tonic-gate 	}
219*7c478bd9Sstevel@tonic-gate 	return (error);
220*7c478bd9Sstevel@tonic-gate }
221*7c478bd9Sstevel@tonic-gate 
222*7c478bd9Sstevel@tonic-gate /*
223*7c478bd9Sstevel@tonic-gate  * Get next component from a path name and leave in
224*7c478bd9Sstevel@tonic-gate  * buffer "component" which should have room for
225*7c478bd9Sstevel@tonic-gate  * MAXNAMELEN bytes (including a null terminator character).
226*7c478bd9Sstevel@tonic-gate  */
227*7c478bd9Sstevel@tonic-gate int
228*7c478bd9Sstevel@tonic-gate pn_getcomponent(struct pathname *pnp, char *component)
229*7c478bd9Sstevel@tonic-gate {
230*7c478bd9Sstevel@tonic-gate 	char c, *cp, *path, saved;
231*7c478bd9Sstevel@tonic-gate 	size_t pathlen;
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate 	path = pnp->pn_path;
234*7c478bd9Sstevel@tonic-gate 	pathlen = pnp->pn_pathlen;
235*7c478bd9Sstevel@tonic-gate 	if (pathlen >= MAXNAMELEN) {
236*7c478bd9Sstevel@tonic-gate 		saved = path[MAXNAMELEN];
237*7c478bd9Sstevel@tonic-gate 		path[MAXNAMELEN] = '/';	/* guarantees loop termination */
238*7c478bd9Sstevel@tonic-gate 		for (cp = path; (c = *cp) != '/'; cp++)
239*7c478bd9Sstevel@tonic-gate 			*component++ = c;
240*7c478bd9Sstevel@tonic-gate 		path[MAXNAMELEN] = saved;
241*7c478bd9Sstevel@tonic-gate 		if (cp - path == MAXNAMELEN)
242*7c478bd9Sstevel@tonic-gate 			return (ENAMETOOLONG);
243*7c478bd9Sstevel@tonic-gate 	} else {
244*7c478bd9Sstevel@tonic-gate 		path[pathlen] = '/';	/* guarantees loop termination */
245*7c478bd9Sstevel@tonic-gate 		for (cp = path; (c = *cp) != '/'; cp++)
246*7c478bd9Sstevel@tonic-gate 			*component++ = c;
247*7c478bd9Sstevel@tonic-gate 		path[pathlen] = '\0';
248*7c478bd9Sstevel@tonic-gate 	}
249*7c478bd9Sstevel@tonic-gate 
250*7c478bd9Sstevel@tonic-gate 	pnp->pn_path = cp;
251*7c478bd9Sstevel@tonic-gate 	pnp->pn_pathlen = pathlen - (cp - path);
252*7c478bd9Sstevel@tonic-gate 	*component = '\0';
253*7c478bd9Sstevel@tonic-gate 	return (0);
254*7c478bd9Sstevel@tonic-gate }
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate /*
257*7c478bd9Sstevel@tonic-gate  * Skip over consecutive slashes in the path name.
258*7c478bd9Sstevel@tonic-gate  */
259*7c478bd9Sstevel@tonic-gate void
260*7c478bd9Sstevel@tonic-gate pn_skipslash(struct pathname *pnp)
261*7c478bd9Sstevel@tonic-gate {
262*7c478bd9Sstevel@tonic-gate 	while (pnp->pn_pathlen > 0 && *pnp->pn_path == '/') {
263*7c478bd9Sstevel@tonic-gate 		pnp->pn_path++;
264*7c478bd9Sstevel@tonic-gate 		pnp->pn_pathlen--;
265*7c478bd9Sstevel@tonic-gate 	}
266*7c478bd9Sstevel@tonic-gate }
267*7c478bd9Sstevel@tonic-gate 
268*7c478bd9Sstevel@tonic-gate /*
269*7c478bd9Sstevel@tonic-gate  * Sets pn_path to the last component in the pathname, updating
270*7c478bd9Sstevel@tonic-gate  * pn_pathlen.  If pathname is empty, or degenerate, leaves pn_path
271*7c478bd9Sstevel@tonic-gate  * pointing at NULL char.  The pathname is explicitly null-terminated
272*7c478bd9Sstevel@tonic-gate  * so that any trailing slashes are effectively removed.
273*7c478bd9Sstevel@tonic-gate  */
274*7c478bd9Sstevel@tonic-gate void
275*7c478bd9Sstevel@tonic-gate pn_setlast(struct pathname *pnp)
276*7c478bd9Sstevel@tonic-gate {
277*7c478bd9Sstevel@tonic-gate 	char *buf = pnp->pn_buf;
278*7c478bd9Sstevel@tonic-gate 	char *path = pnp->pn_path + pnp->pn_pathlen - 1;
279*7c478bd9Sstevel@tonic-gate 	char *endpath;
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate 	while (path > buf && *path == '/')
282*7c478bd9Sstevel@tonic-gate 		--path;
283*7c478bd9Sstevel@tonic-gate 	endpath = path + 1;
284*7c478bd9Sstevel@tonic-gate 	while (path > buf && *path != '/')
285*7c478bd9Sstevel@tonic-gate 		--path;
286*7c478bd9Sstevel@tonic-gate 	if (*path == '/')
287*7c478bd9Sstevel@tonic-gate 		path++;
288*7c478bd9Sstevel@tonic-gate 	*endpath = '\0';
289*7c478bd9Sstevel@tonic-gate 	pnp->pn_path = path;
290*7c478bd9Sstevel@tonic-gate 	pnp->pn_pathlen = endpath - path;
291*7c478bd9Sstevel@tonic-gate }
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate /*
294*7c478bd9Sstevel@tonic-gate  * Eliminate any trailing slashes in the pathname.
295*7c478bd9Sstevel@tonic-gate  * Return non-zero iff there were any trailing slashes.
296*7c478bd9Sstevel@tonic-gate  */
297*7c478bd9Sstevel@tonic-gate int
298*7c478bd9Sstevel@tonic-gate pn_fixslash(struct pathname *pnp)
299*7c478bd9Sstevel@tonic-gate {
300*7c478bd9Sstevel@tonic-gate 	char *start = pnp->pn_path;
301*7c478bd9Sstevel@tonic-gate 	char *end = start + pnp->pn_pathlen;
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 	while (end > start && *(end - 1) == '/')
304*7c478bd9Sstevel@tonic-gate 		end--;
305*7c478bd9Sstevel@tonic-gate 	if (pnp->pn_pathlen == end - start)
306*7c478bd9Sstevel@tonic-gate 		return (0);
307*7c478bd9Sstevel@tonic-gate 	*end = '\0';
308*7c478bd9Sstevel@tonic-gate 	pnp->pn_pathlen = end - start;
309*7c478bd9Sstevel@tonic-gate 	return (1);
310*7c478bd9Sstevel@tonic-gate }
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate /*
313*7c478bd9Sstevel@tonic-gate  * Add a slash to the end of the pathname, if it will fit.
314*7c478bd9Sstevel@tonic-gate  * Return ENAMETOOLONG if it won't.
315*7c478bd9Sstevel@tonic-gate  */
316*7c478bd9Sstevel@tonic-gate int
317*7c478bd9Sstevel@tonic-gate pn_addslash(struct pathname *pnp)
318*7c478bd9Sstevel@tonic-gate {
319*7c478bd9Sstevel@tonic-gate 	if (pnp->pn_path + pnp->pn_pathlen + 1 >=
320*7c478bd9Sstevel@tonic-gate 	    pnp->pn_buf + pnp->pn_bufsize) {
321*7c478bd9Sstevel@tonic-gate 		if (pnp->pn_pathlen + 1 >= pnp->pn_bufsize)	/* no room */
322*7c478bd9Sstevel@tonic-gate 			return (ENAMETOOLONG);
323*7c478bd9Sstevel@tonic-gate 		/*
324*7c478bd9Sstevel@tonic-gate 		 * Move the component to the start of the buffer
325*7c478bd9Sstevel@tonic-gate 		 * so we have room to add the trailing slash.
326*7c478bd9Sstevel@tonic-gate 		 */
327*7c478bd9Sstevel@tonic-gate 		ovbcopy(pnp->pn_path, pnp->pn_buf, pnp->pn_pathlen);
328*7c478bd9Sstevel@tonic-gate 		pnp->pn_path = pnp->pn_buf;
329*7c478bd9Sstevel@tonic-gate 	}
330*7c478bd9Sstevel@tonic-gate 	pnp->pn_path[pnp->pn_pathlen++] = '/';
331*7c478bd9Sstevel@tonic-gate 	pnp->pn_path[pnp->pn_pathlen] = '\0';
332*7c478bd9Sstevel@tonic-gate 	return (0);
333*7c478bd9Sstevel@tonic-gate }
334