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