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