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