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