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
pn_alloc(struct pathname * pnp)73 pn_alloc(struct pathname *pnp)
74 {
75 pn_alloc_sz(pnp, MAXPATHLEN);
76 }
77 void
pn_alloc_sz(struct pathname * pnp,size_t sz)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
pn_free(struct pathname * pnp)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
pn_get_buf(char * str,enum uio_seg seg,struct pathname * pnp,void * buf,size_t bufsize)104 pn_get_buf(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
pn_get(char * str,enum uio_seg seg,struct pathname * pnp)125 pn_get(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
pn_set(struct pathname * pnp,char * path)143 pn_set(struct pathname *pnp, 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
pn_insert(struct pathname * pnp,struct pathname * sympnp,size_t complen)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
pn_getsymlink(vnode_t * vp,struct pathname * pnp,cred_t * crp)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
pn_getcomponent(struct pathname * pnp,char * component)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
pn_skipslash(struct pathname * pnp)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
pn_setlast(struct pathname * pnp)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
pn_fixslash(struct pathname * pnp)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
pn_addslash(struct pathname * pnp)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