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