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
pn_alloc(struct pathname * pnp)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
pn_free(struct pathname * pnp)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
pn_get_buf(char * str,enum uio_seg seg,struct pathname * pnp,void * buf,size_t bufsize)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
pn_get(char * str,enum uio_seg seg,struct pathname * pnp)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
pn_set(struct pathname * pnp,char * path)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
pn_insert(struct pathname * pnp,struct pathname * sympnp,size_t complen)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
pn_getsymlink(vnode_t * vp,struct pathname * pnp,cred_t * crp)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
pn_getcomponent(struct pathname * pnp,char * component)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
pn_skipslash(struct pathname * pnp)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
pn_setlast(struct pathname * pnp)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
pn_fixslash(struct pathname * pnp)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
pn_addslash(struct pathname * pnp)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