1 /* $NetBSD: getcwd.c,v 1.25 2002/04/16 19:08:43 groo Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1991, 1993, 1995 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #if defined(LIBC_SCCS) && !defined(lint) 41 #if 0 42 static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; 43 #else 44 __RCSID("$NetBSD: getcwd.c,v 1.25 2002/04/16 19:08:43 groo Exp $"); 45 #endif 46 #endif /* LIBC_SCCS and not lint */ 47 48 #include "namespace.h" 49 #include <sys/param.h> 50 #include <sys/stat.h> 51 52 #include <assert.h> 53 #include <dirent.h> 54 #include <errno.h> 55 #include <fcntl.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 #include "extern.h" 62 63 #ifdef __weak_alias 64 __weak_alias(getcwd,_getcwd) 65 __weak_alias(realpath,_realpath) 66 #endif 67 68 #define ISDOT(dp) \ 69 (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 70 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 71 72 73 #if defined(__SVR4) || defined(__svr4__) 74 #define d_fileno d_ino 75 #endif 76 77 /* 78 * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); 79 * 80 * Find the real name of path, by removing all ".", ".." and symlink 81 * components. Returns (resolved) on success, or (NULL) on failure, 82 * in which case the path which caused trouble is left in (resolved). 83 */ 84 char * 85 realpath(path, resolved) 86 const char *path; 87 char *resolved; 88 { 89 struct stat sb; 90 int fd, n, rootd, serrno, nlnk = 0; 91 char *p, *q, wbuf[MAXPATHLEN]; 92 93 _DIAGASSERT(path != NULL); 94 _DIAGASSERT(resolved != NULL); 95 96 /* Save the starting point. */ 97 if ((fd = open(".", O_RDONLY)) < 0) { 98 (void)strcpy(resolved, "."); 99 return (NULL); 100 } 101 102 /* 103 * Find the dirname and basename from the path to be resolved. 104 * Change directory to the dirname component. 105 * lstat the basename part. 106 * if it is a symlink, read in the value and loop. 107 * if it is a directory, then change to that directory. 108 * get the current directory name and append the basename. 109 */ 110 (void)strncpy(resolved, path, MAXPATHLEN - 1); 111 resolved[MAXPATHLEN - 1] = '\0'; 112 loop: 113 q = strrchr(resolved, '/'); 114 if (q != NULL) { 115 p = q + 1; 116 if (q == resolved) 117 q = "/"; 118 else { 119 do { 120 --q; 121 } while (q > resolved && *q == '/'); 122 q[1] = '\0'; 123 q = resolved; 124 } 125 if (chdir(q) < 0) 126 goto err1; 127 } else 128 p = resolved; 129 130 /* Deal with the last component. */ 131 if (lstat(p, &sb) == 0) { 132 if (S_ISLNK(sb.st_mode)) { 133 if (nlnk++ >= MAXSYMLINKS) { 134 errno = ELOOP; 135 goto err1; 136 } 137 n = readlink(p, resolved, MAXPATHLEN); 138 if (n < 0) 139 goto err1; 140 resolved[n] = '\0'; 141 goto loop; 142 } 143 if (S_ISDIR(sb.st_mode)) { 144 if (chdir(p) < 0) 145 goto err1; 146 p = ""; 147 } 148 } 149 150 /* 151 * Save the last component name and get the full pathname of 152 * the current directory. 153 */ 154 (void)strlcpy(wbuf, p, sizeof(wbuf)); 155 156 /* 157 * Call the inernal internal version of getcwd which 158 * does a physical search rather than using the $PWD short-cut 159 */ 160 if (getcwd(resolved, MAXPATHLEN) == 0) 161 goto err1; 162 163 /* 164 * Join the two strings together, ensuring that the right thing 165 * happens if the last component is empty, or the dirname is root. 166 */ 167 if (resolved[0] == '/' && resolved[1] == '\0') 168 rootd = 1; 169 else 170 rootd = 0; 171 172 if (*wbuf) { 173 if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) { 174 errno = ENAMETOOLONG; 175 goto err1; 176 } 177 if (rootd == 0) 178 (void)strcat(resolved, "/"); /* XXX: strcat is safe */ 179 (void)strcat(resolved, wbuf); /* XXX: strcat is safe */ 180 } 181 182 /* Go back to where we came from. */ 183 if (fchdir(fd) < 0) { 184 serrno = errno; 185 goto err2; 186 } 187 188 /* It's okay if the close fails, what's an fd more or less? */ 189 (void)close(fd); 190 return (resolved); 191 192 err1: serrno = errno; 193 (void)fchdir(fd); 194 err2: (void)close(fd); 195 errno = serrno; 196 return (NULL); 197 } 198 199 #ifdef OLD_GETCWD 200 201 char * 202 getcwd(pt, size) 203 char *pt; 204 size_t size; 205 { 206 struct dirent *dp; 207 DIR *dir; 208 dev_t dev; 209 ino_t ino; 210 int first; 211 char *bpt, *bup; 212 struct stat s; 213 dev_t root_dev; 214 ino_t root_ino; 215 size_t ptsize, upsize; 216 int save_errno; 217 char *ept, *eup, *up; 218 size_t dlen; 219 220 /* 221 * If no buffer specified by the user, allocate one as necessary. 222 * If a buffer is specified, the size has to be non-zero. The path 223 * is built from the end of the buffer backwards. 224 */ 225 if (pt) { 226 ptsize = 0; 227 if (!size) { 228 errno = EINVAL; 229 return (NULL); 230 } 231 ept = pt + size; 232 } else { 233 if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 234 return (NULL); 235 ept = pt + ptsize; 236 } 237 bpt = ept - 1; 238 *bpt = '\0'; 239 240 /* 241 * Allocate bytes (1024 - malloc space) for the string of "../"'s. 242 * Should always be enough (it's 340 levels). If it's not, allocate 243 * as necessary. Special case the first stat, it's ".", not "..". 244 */ 245 if ((up = malloc(upsize = 1024 - 4)) == NULL) 246 goto err; 247 eup = up + MAXPATHLEN; 248 bup = up; 249 up[0] = '.'; 250 up[1] = '\0'; 251 252 /* Save root values, so know when to stop. */ 253 if (stat("/", &s)) 254 goto err; 255 root_dev = s.st_dev; 256 root_ino = s.st_ino; 257 258 errno = 0; /* XXX readdir has no error return. */ 259 260 for (first = 1;; first = 0) { 261 /* Stat the current level. */ 262 if (lstat(up, &s)) 263 goto err; 264 265 /* Save current node values. */ 266 ino = s.st_ino; 267 dev = s.st_dev; 268 269 /* Check for reaching root. */ 270 if (root_dev == dev && root_ino == ino) { 271 *--bpt = '/'; 272 /* 273 * It's unclear that it's a requirement to copy the 274 * path to the beginning of the buffer, but it's always 275 * been that way and stuff would probably break. 276 */ 277 memmove(pt, bpt, (size_t)(ept - bpt)); 278 free(up); 279 return (pt); 280 } 281 282 /* 283 * Build pointer to the parent directory, allocating memory 284 * as necessary. Max length is 3 for "../", the largest 285 * possible component name, plus a trailing NULL. 286 */ 287 if (bup + 3 + MAXNAMLEN + 1 >= eup) { 288 if ((up = realloc(up, upsize *= 2)) == NULL) 289 goto err; 290 bup = up; 291 eup = up + upsize; 292 } 293 *bup++ = '.'; 294 *bup++ = '.'; 295 *bup = '\0'; 296 297 /* Open and stat parent directory. */ 298 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 299 goto err; 300 301 /* Add trailing slash for next directory. */ 302 *bup++ = '/'; 303 304 /* 305 * If it's a mount point, have to stat each element because 306 * the inode number in the directory is for the entry in the 307 * parent directory, not the inode number of the mounted file. 308 */ 309 save_errno = 0; 310 if (s.st_dev == dev) { 311 for (;;) { 312 if (!(dp = readdir(dir))) 313 goto notfound; 314 if (dp->d_fileno == ino) { 315 #if defined(__SVR4) || defined(__svr4__) || defined(__linux__) 316 dlen = strlen(dp->d_name); 317 #else 318 dlen = dp->d_namlen; 319 #endif 320 break; 321 } 322 } 323 } else 324 for (;;) { 325 if (!(dp = readdir(dir))) 326 goto notfound; 327 if (ISDOT(dp)) 328 continue; 329 #if defined(__SVR4) || defined(__svr4__) || defined(__linux__) 330 dlen = strlen(dp->d_name); 331 #else 332 dlen = dp->d_namlen; 333 #endif 334 memmove(bup, dp->d_name, dlen + 1); 335 336 /* Save the first error for later. */ 337 if (lstat(up, &s)) { 338 if (!save_errno) 339 save_errno = errno; 340 errno = 0; 341 continue; 342 } 343 if (s.st_dev == dev && s.st_ino == ino) 344 break; 345 } 346 347 /* 348 * Check for length of the current name, preceding slash, 349 * leading slash. 350 */ 351 if (bpt - pt <= dlen + (first ? 1 : 2)) { 352 size_t len, off; 353 354 if (!ptsize) { 355 errno = ERANGE; 356 goto err; 357 } 358 off = bpt - pt; 359 len = ept - bpt; 360 if ((pt = realloc(pt, ptsize *= 2)) == NULL) 361 goto err; 362 bpt = pt + off; 363 ept = pt + ptsize; 364 memmove(ept - len, bpt, len); 365 bpt = ept - len; 366 } 367 if (!first) 368 *--bpt = '/'; 369 bpt -= dlen; 370 memmove(bpt, dp->d_name, dlen); 371 (void)closedir(dir); 372 373 /* Truncate any file name. */ 374 *bup = '\0'; 375 } 376 377 notfound: 378 /* 379 * If readdir set errno, use it, not any saved error; otherwise, 380 * didn't find the current directory in its parent directory, set 381 * errno to ENOENT. 382 */ 383 if (!errno) 384 errno = save_errno ? save_errno : ENOENT; 385 /* FALLTHROUGH */ 386 err: 387 if (ptsize) 388 free(pt); 389 free(up); 390 return (NULL); 391 } 392 393 #else /* New getcwd */ 394 395 char * 396 getcwd(pt, size) 397 char *pt; 398 size_t size; 399 { 400 size_t ptsize, bufsize; 401 int len; 402 403 /* 404 * If no buffer specified by the user, allocate one as necessary. 405 * If a buffer is specified, the size has to be non-zero. The path 406 * is built from the end of the buffer backwards. 407 */ 408 if (pt) { 409 ptsize = 0; 410 if (!size) { 411 errno = EINVAL; 412 return (NULL); 413 } 414 bufsize = size; 415 } else { 416 if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 417 return (NULL); 418 bufsize = ptsize; 419 } 420 for (;;) { 421 len = __getcwd(pt, bufsize); 422 if ((len < 0) && (size == 0) && (errno == ERANGE)) { 423 if (ptsize > (MAXPATHLEN*4)) 424 return NULL; 425 if ((pt = realloc(pt, ptsize *= 2)) == NULL) 426 return NULL; 427 bufsize = ptsize; 428 continue; 429 } 430 break; 431 } 432 if (len < 0) 433 return NULL; 434 else 435 return pt; 436 } 437 438 #endif 439