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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 #pragma ident "%Z%%M% %I% %E% SMI" 23 24 /* 25 * Copyright (c) 1987 by Sun Microsystems, Inc. 26 */ 27 28 #include <strings.h> 29 #include <sys/param.h> 30 #include <errno.h> 31 32 extern char *getwd(); 33 34 /* LINTLIBRARY */ 35 36 /* 37 * Input name in raw, canonicalized pathname output to canon. If dosymlinks 38 * is nonzero, resolves all symbolic links encountered during canonicalization 39 * into an equivalent symlink-free form. Returns 0 on success, -1 on failure. 40 * The routine fails if the current working directory can't be obtained or if 41 * either of the arguments is NULL. 42 * 43 * Sets errno on failure. 44 */ 45 int 46 pathcanon(raw, canon, dosymlinks) 47 char *raw, 48 *canon; 49 int dosymlinks; 50 { 51 register char *s, 52 *d; 53 register char *limit = canon + MAXPATHLEN; 54 char *modcanon; 55 int nlink = 0; 56 57 /* 58 * Do a bit of sanity checking. 59 */ 60 if (raw == NULL || canon == NULL) { 61 errno = EINVAL; 62 return (-1); 63 } 64 65 /* 66 * If the path in raw is not already absolute, convert it to that form. 67 * In any case, initialize canon with the absolute form of raw. Make 68 * sure that none of the operations overflow the corresponding buffers. 69 * The code below does the copy operations by hand so that it can easily 70 * keep track of whether overflow is about to occur. 71 */ 72 s = raw; 73 d = canon; 74 if (*s != '/') { 75 /* Relative; prepend the working directory. */ 76 if (getwd(d) == NULL) { 77 /* Use whatever errno value getwd may have left around. */ 78 return (-1); 79 } 80 d += strlen(d); 81 /* Add slash to separate working directory from relative part. */ 82 if (d < limit) 83 *d++ = '/'; 84 modcanon = d; 85 } else 86 modcanon = canon; 87 while (d < limit && *s) 88 *d++ = *s++; 89 90 /* Add a trailing slash to simplify the code below. */ 91 s = "/"; 92 while (d < limit && (*d++ = *s++)) 93 continue; 94 95 96 /* 97 * Canonicalize the path. The strategy is to update in place, with 98 * d pointing to the end of the canonicalized portion and s to the 99 * current spot from which we're copying. This works because 100 * canonicalization doesn't increase path length, except as discussed 101 * below. Note also that the path has had a slash added at its end. 102 * This greatly simplifies the treatment of boundary conditions. 103 */ 104 d = s = modcanon; 105 while (d < limit && *s) { 106 if ((*d++ = *s++) == '/' && d > canon + 1) { 107 register char *t = d - 2; 108 109 switch (*t) { 110 case '/': 111 /* Found // in the name. */ 112 d--; 113 continue; 114 case '.': 115 switch (*--t) { 116 case '/': 117 /* Found /./ in the name. */ 118 d -= 2; 119 continue; 120 case '.': 121 if (*--t == '/') { 122 /* Found /../ in the name. */ 123 while (t > canon && *--t != '/') 124 continue; 125 d = t + 1; 126 } 127 continue; 128 default: 129 break; 130 } 131 break; 132 default: 133 break; 134 } 135 /* 136 * We're at the end of a component. If dosymlinks is set 137 * see whether the component is a symbolic link. If so, 138 * replace it by its contents. 139 */ 140 if (dosymlinks) { 141 char link[MAXPATHLEN + 1]; 142 register int llen; 143 144 /* 145 * See whether it's a symlink by trying to read it. 146 * 147 * Start by isolating it. 148 */ 149 *(d - 1) = '\0'; 150 if ((llen = readlink(canon, link, sizeof link)) >= 0) { 151 /* Make sure that there are no circular links. */ 152 nlink++; 153 if (nlink > MAXSYMLINKS) { 154 errno = ELOOP; 155 return (-1); 156 } 157 /* 158 * The component is a symlink. Since its value can be 159 * of arbitrary size, we can't continue copying in place. 160 * Instead, form the new path suffix in the link buffer 161 * and then copy it back to its proper spot in canon. 162 */ 163 t = link + llen; 164 *t++ = '/'; 165 /* 166 * Copy the remaining unresolved portion to the end 167 * of the symlink. If the sum of the unresolved part and 168 * the readlink exceeds MAXPATHLEN, the extra bytes 169 * will be dropped off. Too bad! 170 */ 171 (void) strncpy(t, s, sizeof link - llen - 1); 172 link[sizeof link - 1] = '\0'; 173 /* 174 * If the link's contents are absolute, copy it back 175 * to the start of canon, otherwise to the beginning of 176 * the link's position in the path. 177 */ 178 if (link[0] == '/') { 179 /* Absolute. */ 180 (void) strcpy(canon, link); 181 d = s = canon; 182 } 183 else { 184 /* 185 * Relative: find beginning of component and copy. 186 */ 187 --d; 188 while (d > canon && *--d != '/') 189 continue; 190 s = ++d; 191 /* 192 * If the sum of the resolved part, the readlink 193 * and the remaining unresolved part exceeds 194 * MAXPATHLEN, the extra bytes will be dropped off. 195 */ 196 if (strlen(link) >= (limit - s)) { 197 (void) strncpy(s, link, limit - s); 198 *(limit - 1) = '\0'; 199 } else { 200 (void) strcpy(s, link); 201 } 202 } 203 continue; 204 } else { 205 /* 206 * readlink call failed. It can be because it was 207 * not a link (i.e. a file, dir etc.) or because the 208 * the call actually failed. 209 */ 210 if (errno != EINVAL) 211 return (-1); 212 *(d - 1) = '/'; /* Restore it */ 213 } 214 } /* if (dosymlinks) */ 215 } 216 } /* while */ 217 218 /* Remove the trailing slash that was added above. */ 219 if (*(d - 1) == '/' && d > canon + 1) 220 d--; 221 *d = '\0'; 222 return (0); 223 } 224 225 /* 226 * Canonicalize the path given in raw, resolving away all symbolic link 227 * components. Store the result into the buffer named by canon, which 228 * must be long enough (MAXPATHLEN bytes will suffice). Returns NULL 229 * on failure and canon on success. 230 * 231 * The routine indirectly invokes the readlink() system call and getwd() 232 * so it inherits the possibility of hanging due to inaccessible file 233 * system resources. 234 */ 235 char * 236 realpath(raw, canon) 237 char *raw; 238 char *canon; 239 { 240 return (pathcanon(raw, canon, 1) < 0 ? NULL : canon); 241 } 242