getcwd.c revision 23661
11573Srgrimes/* 223661Speter * Copyright (c) 1989, 1991, 1993, 1995 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 523661Speter * This code is derived from software contributed to Berkeley by 623661Speter * Jan-Simon Pendry. 723661Speter * 81573Srgrimes * Redistribution and use in source and binary forms, with or without 91573Srgrimes * modification, are permitted provided that the following conditions 101573Srgrimes * are met: 111573Srgrimes * 1. Redistributions of source code must retain the above copyright 121573Srgrimes * notice, this list of conditions and the following disclaimer. 131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141573Srgrimes * notice, this list of conditions and the following disclaimer in the 151573Srgrimes * documentation and/or other materials provided with the distribution. 161573Srgrimes * 3. All advertising materials mentioning features or use of this software 171573Srgrimes * must display the following acknowledgement: 181573Srgrimes * This product includes software developed by the University of 191573Srgrimes * California, Berkeley and its contributors. 201573Srgrimes * 4. Neither the name of the University nor the names of its contributors 211573Srgrimes * may be used to endorse or promote products derived from this software 221573Srgrimes * without specific prior written permission. 231573Srgrimes * 241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341573Srgrimes * SUCH DAMAGE. 351573Srgrimes */ 361573Srgrimes 371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 3823661Speterstatic char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; 391573Srgrimes#endif /* LIBC_SCCS and not lint */ 401573Srgrimes 411573Srgrimes#include <sys/param.h> 421573Srgrimes#include <sys/stat.h> 431573Srgrimes 4423661Speter#include <dirent.h> 451573Srgrimes#include <errno.h> 4623661Speter#include <fcntl.h> 471573Srgrimes#include <stdio.h> 481573Srgrimes#include <stdlib.h> 491573Srgrimes#include <string.h> 501573Srgrimes#include <unistd.h> 511573Srgrimes 5223661Speterstatic char *getcwd_physical __P((char *, size_t)); 5323661Speter 541573Srgrimes#define ISDOT(dp) \ 551573Srgrimes (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 5617141Sjkh (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 571573Srgrimes 581573Srgrimeschar * 591573Srgrimesgetcwd(pt, size) 601573Srgrimes char *pt; 611573Srgrimes size_t size; 621573Srgrimes{ 6323661Speter char *pwd; 6423661Speter size_t pwdlen; 6523661Speter dev_t dev; 6623661Speter ino_t ino; 6723661Speter struct stat s; 6823661Speter 6923661Speter /* Check $PWD -- if it's right, it's fast. */ 7023661Speter if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) { 7123661Speter dev = s.st_dev; 7223661Speter ino = s.st_ino; 7323661Speter if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) { 7423661Speter pwdlen = strlen(pwd); 7523661Speter if (size != 0) { 7623661Speter if (pwdlen + 1 > size) { 7723661Speter errno = ERANGE; 7823661Speter return (NULL); 7923661Speter } 8023661Speter } else if ((pt = malloc(pwdlen + 1)) == NULL) 8123661Speter return (NULL); 8223661Speter memmove(pt, pwd, pwdlen); 8323661Speter pt[pwdlen] = '\0'; 8423661Speter return (pt); 8523661Speter } 8623661Speter } 8723661Speter 8823661Speter return (getcwd_physical(pt, size)); 8923661Speter} 9023661Speter 9123661Speter/* 9223661Speter * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); 9323661Speter * 9423661Speter * Find the real name of path, by removing all ".", ".." and symlink 9523661Speter * components. Returns (resolved) on success, or (NULL) on failure, 9623661Speter * in which case the path which caused trouble is left in (resolved). 9723661Speter */ 9823661Speterchar * 9923661Speterrealpath(path, resolved) 10023661Speter const char *path; 10123661Speter char *resolved; 10223661Speter{ 10323661Speter struct stat sb; 10423661Speter int fd, n, rootd, serrno; 10523661Speter char *p, *q, wbuf[MAXPATHLEN]; 10623661Speter 10723661Speter /* Save the starting point. */ 10823661Speter if ((fd = open(".", O_RDONLY)) < 0) { 10923661Speter (void)strcpy(resolved, "."); 11023661Speter return (NULL); 11123661Speter } 11223661Speter 11323661Speter /* 11423661Speter * Find the dirname and basename from the path to be resolved. 11523661Speter * Change directory to the dirname component. 11623661Speter * lstat the basename part. 11723661Speter * if it is a symlink, read in the value and loop. 11823661Speter * if it is a directory, then change to that directory. 11923661Speter * get the current directory name and append the basename. 12023661Speter */ 12123661Speter (void)strncpy(resolved, path, MAXPATHLEN - 1); 12223661Speter resolved[MAXPATHLEN - 1] = '\0'; 12323661Speterloop: 12423661Speter q = strrchr(resolved, '/'); 12523661Speter if (q != NULL) { 12623661Speter p = q + 1; 12723661Speter if (q == resolved) 12823661Speter q = "/"; 12923661Speter else { 13023661Speter do { 13123661Speter --q; 13223661Speter } while (q > resolved && *q == '/'); 13323661Speter q[1] = '\0'; 13423661Speter q = resolved; 13523661Speter } 13623661Speter if (chdir(q) < 0) 13723661Speter goto err1; 13823661Speter } else 13923661Speter p = resolved; 14023661Speter 14123661Speter /* Deal with the last component. */ 14223661Speter if (*p != '\0' && lstat(p, &sb) == 0) { 14323661Speter if (S_ISLNK(sb.st_mode)) { 14423661Speter n = readlink(p, resolved, MAXPATHLEN); 14523661Speter if (n < 0) 14623661Speter goto err1; 14723661Speter resolved[n] = '\0'; 14823661Speter goto loop; 14923661Speter } 15023661Speter if (S_ISDIR(sb.st_mode)) { 15123661Speter if (chdir(p) < 0) 15223661Speter goto err1; 15323661Speter p = ""; 15423661Speter } 15523661Speter } 15623661Speter 15723661Speter /* 15823661Speter * Save the last component name and get the full pathname of 15923661Speter * the current directory. 16023661Speter */ 16123661Speter (void)strcpy(wbuf, p); 16223661Speter 16323661Speter /* 16423661Speter * Call the inernal internal version of getcwd which 16523661Speter * does a physical search rather than using the $PWD short-cut 16623661Speter */ 16723661Speter if (getcwd_physical(resolved, MAXPATHLEN) == 0) 16823661Speter goto err1; 16923661Speter 17023661Speter /* 17123661Speter * Join the two strings together, ensuring that the right thing 17223661Speter * happens if the last component is empty, or the dirname is root. 17323661Speter */ 17423661Speter if (resolved[0] == '/' && resolved[1] == '\0') 17523661Speter rootd = 1; 17623661Speter else 17723661Speter rootd = 0; 17823661Speter 17923661Speter if (*wbuf) { 18023661Speter if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) { 18123661Speter errno = ENAMETOOLONG; 18223661Speter goto err1; 18323661Speter } 18423661Speter if (rootd == 0) 18523661Speter (void)strcat(resolved, "/"); 18623661Speter (void)strcat(resolved, wbuf); 18723661Speter } 18823661Speter 18923661Speter /* Go back to where we came from. */ 19023661Speter if (fchdir(fd) < 0) { 19123661Speter serrno = errno; 19223661Speter goto err2; 19323661Speter } 19423661Speter 19523661Speter /* It's okay if the close fails, what's an fd more or less? */ 19623661Speter (void)close(fd); 19723661Speter return (resolved); 19823661Speter 19923661Spetererr1: serrno = errno; 20023661Speter (void)fchdir(fd); 20123661Spetererr2: (void)close(fd); 20223661Speter errno = serrno; 20323661Speter return (NULL); 20423661Speter} 20523661Speter 20623661Speterstatic char * 20723661Spetergetcwd_physical(pt, size) 20823661Speter char *pt; 20923661Speter size_t size; 21023661Speter{ 2111573Srgrimes register struct dirent *dp; 2121573Srgrimes register DIR *dir; 2131573Srgrimes register dev_t dev; 2141573Srgrimes register ino_t ino; 2151573Srgrimes register int first; 2161573Srgrimes register char *bpt, *bup; 2171573Srgrimes struct stat s; 2181573Srgrimes dev_t root_dev; 2191573Srgrimes ino_t root_ino; 2206227Sdg size_t ptsize, upsize; 2211573Srgrimes int save_errno; 2226227Sdg char *ept, *eup, *up; 2231573Srgrimes 2241573Srgrimes /* 2251573Srgrimes * If no buffer specified by the user, allocate one as necessary. 2261573Srgrimes * If a buffer is specified, the size has to be non-zero. The path 2271573Srgrimes * is built from the end of the buffer backwards. 2281573Srgrimes */ 2291573Srgrimes if (pt) { 2301573Srgrimes ptsize = 0; 2311573Srgrimes if (!size) { 2321573Srgrimes errno = EINVAL; 2331573Srgrimes return (NULL); 2341573Srgrimes } 2355072Sbde if (size == 1) { 2365072Sbde errno = ERANGE; 2375072Sbde return (NULL); 2385072Sbde } 2391573Srgrimes ept = pt + size; 2401573Srgrimes } else { 2411573Srgrimes if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 2421573Srgrimes return (NULL); 2431573Srgrimes ept = pt + ptsize; 2441573Srgrimes } 2451573Srgrimes bpt = ept - 1; 2461573Srgrimes *bpt = '\0'; 2471573Srgrimes 2481573Srgrimes /* 2491573Srgrimes * Allocate bytes (1024 - malloc space) for the string of "../"'s. 2501573Srgrimes * Should always be enough (it's 340 levels). If it's not, allocate 25123661Speter * as necessary. Special case the first stat, it's ".", not "..". 2521573Srgrimes */ 2531573Srgrimes if ((up = malloc(upsize = 1024 - 4)) == NULL) 2541573Srgrimes goto err; 2551573Srgrimes eup = up + MAXPATHLEN; 2561573Srgrimes bup = up; 2571573Srgrimes up[0] = '.'; 2581573Srgrimes up[1] = '\0'; 2591573Srgrimes 2601573Srgrimes /* Save root values, so know when to stop. */ 2611573Srgrimes if (stat("/", &s)) 2621573Srgrimes goto err; 2631573Srgrimes root_dev = s.st_dev; 2641573Srgrimes root_ino = s.st_ino; 2651573Srgrimes 2661573Srgrimes errno = 0; /* XXX readdir has no error return. */ 2671573Srgrimes 2681573Srgrimes for (first = 1;; first = 0) { 2691573Srgrimes /* Stat the current level. */ 2701573Srgrimes if (lstat(up, &s)) 2711573Srgrimes goto err; 2721573Srgrimes 2731573Srgrimes /* Save current node values. */ 2741573Srgrimes ino = s.st_ino; 2751573Srgrimes dev = s.st_dev; 2761573Srgrimes 2771573Srgrimes /* Check for reaching root. */ 2781573Srgrimes if (root_dev == dev && root_ino == ino) { 2791573Srgrimes *--bpt = '/'; 2801573Srgrimes /* 2811573Srgrimes * It's unclear that it's a requirement to copy the 2821573Srgrimes * path to the beginning of the buffer, but it's always 2831573Srgrimes * been that way and stuff would probably break. 2841573Srgrimes */ 2859272Shsu bcopy(bpt, pt, ept - bpt); 2861573Srgrimes free(up); 2871573Srgrimes return (pt); 2881573Srgrimes } 2891573Srgrimes 2901573Srgrimes /* 2911573Srgrimes * Build pointer to the parent directory, allocating memory 2921573Srgrimes * as necessary. Max length is 3 for "../", the largest 2931573Srgrimes * possible component name, plus a trailing NULL. 2941573Srgrimes */ 2951573Srgrimes if (bup + 3 + MAXNAMLEN + 1 >= eup) { 2961573Srgrimes if ((up = realloc(up, upsize *= 2)) == NULL) 2971573Srgrimes goto err; 2981573Srgrimes bup = up; 2991573Srgrimes eup = up + upsize; 3001573Srgrimes } 3011573Srgrimes *bup++ = '.'; 3021573Srgrimes *bup++ = '.'; 3031573Srgrimes *bup = '\0'; 3041573Srgrimes 3051573Srgrimes /* Open and stat parent directory. */ 3061573Srgrimes if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 3071573Srgrimes goto err; 3081573Srgrimes 3091573Srgrimes /* Add trailing slash for next directory. */ 3101573Srgrimes *bup++ = '/'; 31118971Speter *bup = '\0'; 3121573Srgrimes 3131573Srgrimes /* 3141573Srgrimes * If it's a mount point, have to stat each element because 3151573Srgrimes * the inode number in the directory is for the entry in the 3161573Srgrimes * parent directory, not the inode number of the mounted file. 3171573Srgrimes */ 3181573Srgrimes save_errno = 0; 3191573Srgrimes if (s.st_dev == dev) { 3201573Srgrimes for (;;) { 3211573Srgrimes if (!(dp = readdir(dir))) 3221573Srgrimes goto notfound; 3231573Srgrimes if (dp->d_fileno == ino) 3241573Srgrimes break; 3251573Srgrimes } 3261573Srgrimes } else 3271573Srgrimes for (;;) { 3281573Srgrimes if (!(dp = readdir(dir))) 3291573Srgrimes goto notfound; 3301573Srgrimes if (ISDOT(dp)) 3311573Srgrimes continue; 3321573Srgrimes bcopy(dp->d_name, bup, dp->d_namlen + 1); 3331573Srgrimes 3341573Srgrimes /* Save the first error for later. */ 3351573Srgrimes if (lstat(up, &s)) { 3361573Srgrimes if (!save_errno) 3371573Srgrimes save_errno = errno; 3381573Srgrimes errno = 0; 3391573Srgrimes continue; 3401573Srgrimes } 3411573Srgrimes if (s.st_dev == dev && s.st_ino == ino) 3421573Srgrimes break; 3431573Srgrimes } 3441573Srgrimes 3451573Srgrimes /* 3461573Srgrimes * Check for length of the current name, preceding slash, 3471573Srgrimes * leading slash. 3481573Srgrimes */ 3495072Sbde if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 3501573Srgrimes size_t len, off; 3511573Srgrimes 3521573Srgrimes if (!ptsize) { 3531573Srgrimes errno = ERANGE; 3541573Srgrimes goto err; 3551573Srgrimes } 3561573Srgrimes off = bpt - pt; 3571573Srgrimes len = ept - bpt; 3581573Srgrimes if ((pt = realloc(pt, ptsize *= 2)) == NULL) 3591573Srgrimes goto err; 3601573Srgrimes bpt = pt + off; 3611573Srgrimes ept = pt + ptsize; 3629272Shsu bcopy(bpt, ept - len, len); 3631573Srgrimes bpt = ept - len; 3641573Srgrimes } 3651573Srgrimes if (!first) 3661573Srgrimes *--bpt = '/'; 3671573Srgrimes bpt -= dp->d_namlen; 3681573Srgrimes bcopy(dp->d_name, bpt, dp->d_namlen); 3691573Srgrimes (void)closedir(dir); 3701573Srgrimes 3711573Srgrimes /* Truncate any file name. */ 3721573Srgrimes *bup = '\0'; 3731573Srgrimes } 3741573Srgrimes 3751573Srgrimesnotfound: 3761573Srgrimes /* 3771573Srgrimes * If readdir set errno, use it, not any saved error; otherwise, 3781573Srgrimes * didn't find the current directory in its parent directory, set 3791573Srgrimes * errno to ENOENT. 3801573Srgrimes */ 3811573Srgrimes if (!errno) 3821573Srgrimes errno = save_errno ? save_errno : ENOENT; 3831573Srgrimes /* FALLTHROUGH */ 3841573Srgrimeserr: 3851573Srgrimes if (ptsize) 3861573Srgrimes free(pt); 3871573Srgrimes free(up); 3881573Srgrimes return (NULL); 3891573Srgrimes} 390