Home | History | Annotate | Download | only in fs
      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	"@(#)pathname.c	1.23	07/10/25 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