121345Sdist /* 268218Sbostic * Copyright (c) 1989, 1991, 1993, 1995 361111Sbostic * The Regents of the University of California. All rights reserved. 437209Sbostic * 5*68223Spendry * This code is derived from software contributed to Berkeley by 6*68223Spendry * Jan-Simon Pendry. 7*68223Spendry * 842625Sbostic * %sccs.include.redist.c% 921345Sdist */ 109986Ssam 1126565Sdonn #if defined(LIBC_SCCS) && !defined(lint) 12*68223Spendry static char sccsid[] = "@(#)getcwd.c 8.4 (Berkeley) 02/03/95"; 1337209Sbostic #endif /* LIBC_SCCS and not lint */ 1421345Sdist 1510088Ssam #include <sys/param.h> 1610088Ssam #include <sys/stat.h> 1754722Sbostic 18*68223Spendry #include <dirent.h> 1946483Sbostic #include <errno.h> 20*68223Spendry #include <fcntl.h> 2146483Sbostic #include <stdio.h> 2246483Sbostic #include <stdlib.h> 2342396Sbostic #include <string.h> 2446597Sdonn #include <unistd.h> 259986Ssam 26*68223Spendry static char *getcwd_physical __P((char *, size_t)); 27*68223Spendry 2842396Sbostic #define ISDOT(dp) \ 2942396Sbostic (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 3042396Sbostic dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 3142396Sbostic 329986Ssam char * 3346483Sbostic getcwd(pt, size) 3446483Sbostic char *pt; 3546483Sbostic size_t size; 369986Ssam { 37*68223Spendry char *pwd; 38*68223Spendry size_t pwdlen; 39*68223Spendry dev_t dev; 40*68223Spendry ino_t ino; 4142396Sbostic struct stat s; 429986Ssam 4368218Sbostic /* Check $PWD -- if it's right, it's fast. */ 4468219Sbostic if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) { 4568218Sbostic dev = s.st_dev; 4668218Sbostic ino = s.st_ino; 4768218Sbostic if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) { 4868218Sbostic pwdlen = strlen(pwd); 4968218Sbostic if (size != 0) { 5068218Sbostic if (pwdlen + 1 > size) { 5168218Sbostic errno = ERANGE; 5268218Sbostic return (NULL); 5368218Sbostic } 5468218Sbostic } else if ((pt = malloc(pwdlen + 1)) == NULL) 5568218Sbostic return (NULL); 5668218Sbostic memmove(pt, pwd, pwdlen); 5768218Sbostic pwd[pwdlen] = '\0'; 5868218Sbostic return (pt); 5968218Sbostic } 6068218Sbostic } 6168218Sbostic 62*68223Spendry return (getcwd_physical(pt, size)); 63*68223Spendry } 64*68223Spendry 65*68223Spendry /* 66*68223Spendry * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); 67*68223Spendry * 68*68223Spendry * Find the real name of path, by removing all ".", ".." and symlink 69*68223Spendry * components. Returns (resolved) on success, or (NULL) on failure, 70*68223Spendry * in which case the path which caused trouble is left in (resolved). 71*68223Spendry */ 72*68223Spendry char * 73*68223Spendry realpath(path, resolved) 74*68223Spendry const char *path; 75*68223Spendry char *resolved; 76*68223Spendry { 77*68223Spendry struct stat sb; 78*68223Spendry int fd, n, rootd, serrno; 79*68223Spendry char *p, *q, wbuf[MAXPATHLEN]; 80*68223Spendry 81*68223Spendry /* Save the starting point. */ 82*68223Spendry if ((fd = open(".", O_RDONLY)) < 0) { 83*68223Spendry (void)strcpy(resolved, "."); 84*68223Spendry return (NULL); 85*68223Spendry } 86*68223Spendry 8746483Sbostic /* 88*68223Spendry * Find the dirname and basename from the path to be resolved. 89*68223Spendry * Change directory to the dirname component. 90*68223Spendry * lstat the basename part. 91*68223Spendry * if it is a symlink, read in the value and loop. 92*68223Spendry * if it is a directory, then change to that directory. 93*68223Spendry * get the current directory name and append the basename. 94*68223Spendry */ 95*68223Spendry (void)strncpy(resolved, path, MAXPATHLEN - 1); 96*68223Spendry resolved[MAXPATHLEN - 1] = '\0'; 97*68223Spendry loop: 98*68223Spendry q = strrchr(resolved, '/'); 99*68223Spendry if (q != NULL) { 100*68223Spendry p = q + 1; 101*68223Spendry if (q == resolved) 102*68223Spendry q = "/"; 103*68223Spendry else { 104*68223Spendry do { 105*68223Spendry --q; 106*68223Spendry } while (q > resolved && *q == '/'); 107*68223Spendry q[1] = '\0'; 108*68223Spendry q = resolved; 109*68223Spendry } 110*68223Spendry if (chdir(q) < 0) 111*68223Spendry goto err1; 112*68223Spendry } else 113*68223Spendry p = resolved; 114*68223Spendry 115*68223Spendry /* Deal with the last component. */ 116*68223Spendry if (lstat(p, &sb) == 0) { 117*68223Spendry if (S_ISLNK(sb.st_mode)) { 118*68223Spendry n = readlink(p, resolved, MAXPATHLEN); 119*68223Spendry if (n < 0) 120*68223Spendry goto err1; 121*68223Spendry resolved[n] = '\0'; 122*68223Spendry goto loop; 123*68223Spendry } 124*68223Spendry if (S_ISDIR(sb.st_mode)) { 125*68223Spendry if (chdir(p) < 0) 126*68223Spendry goto err1; 127*68223Spendry p = ""; 128*68223Spendry } 129*68223Spendry } 130*68223Spendry 131*68223Spendry /* 132*68223Spendry * Save the last component name and get the full pathname of 133*68223Spendry * the current directory. 134*68223Spendry */ 135*68223Spendry (void)strcpy(wbuf, p); 136*68223Spendry 137*68223Spendry /* 138*68223Spendry * Call the inernal internal version of getcwd which 139*68223Spendry * does a physical search rather than using the $PWD short-cut 140*68223Spendry */ 141*68223Spendry if (getcwd_physical(resolved, MAXPATHLEN) == 0) 142*68223Spendry goto err1; 143*68223Spendry 144*68223Spendry /* 145*68223Spendry * Join the two strings together, ensuring that the right thing 146*68223Spendry * happens if the last component is empty, or the dirname is root. 147*68223Spendry */ 148*68223Spendry if (resolved[0] == '/' && resolved[1] == '\0') 149*68223Spendry rootd = 1; 150*68223Spendry else 151*68223Spendry rootd = 0; 152*68223Spendry 153*68223Spendry if (*wbuf) { 154*68223Spendry if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) { 155*68223Spendry errno = ENAMETOOLONG; 156*68223Spendry goto err1; 157*68223Spendry } 158*68223Spendry if (rootd == 0) 159*68223Spendry (void)strcat(resolved, "/"); 160*68223Spendry (void)strcat(resolved, wbuf); 161*68223Spendry } 162*68223Spendry 163*68223Spendry /* Go back to where we came from. */ 164*68223Spendry if (fchdir(fd) < 0) { 165*68223Spendry serrno = errno; 166*68223Spendry goto err2; 167*68223Spendry } 168*68223Spendry 169*68223Spendry /* It's okay if the close fails, what's an fd more or less? */ 170*68223Spendry (void)close(fd); 171*68223Spendry return (resolved); 172*68223Spendry 173*68223Spendry err1: serrno = errno; 174*68223Spendry (void)fchdir(fd); 175*68223Spendry err2: (void)close(fd); 176*68223Spendry errno = serrno; 177*68223Spendry return (NULL); 178*68223Spendry } 179*68223Spendry 180*68223Spendry static char * 181*68223Spendry getcwd_physical(pt, size) 182*68223Spendry char *pt; 183*68223Spendry size_t size; 184*68223Spendry { 185*68223Spendry register struct dirent *dp; 186*68223Spendry register DIR *dir; 187*68223Spendry register dev_t dev; 188*68223Spendry register ino_t ino; 189*68223Spendry register int first; 190*68223Spendry register char *bpt, *bup; 191*68223Spendry struct stat s; 192*68223Spendry dev_t root_dev; 193*68223Spendry ino_t root_ino; 194*68223Spendry size_t ptsize, upsize; 195*68223Spendry int save_errno; 196*68223Spendry char *ept, *eup, *up; 197*68223Spendry 198*68223Spendry /* 19946483Sbostic * If no buffer specified by the user, allocate one as necessary. 20046483Sbostic * If a buffer is specified, the size has to be non-zero. The path 20146483Sbostic * is built from the end of the buffer backwards. 20246483Sbostic */ 20346483Sbostic if (pt) { 20446483Sbostic ptsize = 0; 20546483Sbostic if (!size) { 20646483Sbostic errno = EINVAL; 20754722Sbostic return (NULL); 20846483Sbostic } 20946483Sbostic ept = pt + size; 21046483Sbostic } else { 21154722Sbostic if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 21254722Sbostic return (NULL); 21346483Sbostic ept = pt + ptsize; 21446483Sbostic } 21546483Sbostic bpt = ept - 1; 21646645Sbostic *bpt = '\0'; 21746483Sbostic 21846483Sbostic /* 21946483Sbostic * Allocate bytes (1024 - malloc space) for the string of "../"'s. 22046483Sbostic * Should always be enough (it's 340 levels). If it's not, allocate 22168218Sbostic * as necessary. Special case the first stat, it's ".", not "..". 22246483Sbostic */ 22354722Sbostic if ((up = malloc(upsize = 1024 - 4)) == NULL) 22437209Sbostic goto err; 22546483Sbostic eup = up + MAXPATHLEN; 22646483Sbostic bup = up; 22746483Sbostic up[0] = '.'; 22846483Sbostic up[1] = '\0'; 22946483Sbostic 23046483Sbostic /* Save root values, so know when to stop. */ 23146483Sbostic if (stat("/", &s)) 23246483Sbostic goto err; 23337209Sbostic root_dev = s.st_dev; 23437209Sbostic root_ino = s.st_ino; 23542396Sbostic 23646483Sbostic errno = 0; /* XXX readdir has no error return. */ 23742396Sbostic 23846483Sbostic for (first = 1;; first = 0) { 23946483Sbostic /* Stat the current level. */ 24046483Sbostic if (lstat(up, &s)) 24142396Sbostic goto err; 24242396Sbostic 24346483Sbostic /* Save current node values. */ 24442396Sbostic ino = s.st_ino; 24542396Sbostic dev = s.st_dev; 24642396Sbostic 24746483Sbostic /* Check for reaching root. */ 24842396Sbostic if (root_dev == dev && root_ino == ino) { 24946483Sbostic *--bpt = '/'; 25046483Sbostic /* 25146483Sbostic * It's unclear that it's a requirement to copy the 25246483Sbostic * path to the beginning of the buffer, but it's always 25346483Sbostic * been that way and stuff would probably break. 25446483Sbostic */ 25546483Sbostic (void)bcopy(bpt, pt, ept - bpt); 25646483Sbostic free(up); 25754722Sbostic return (pt); 25810155Ssam } 25942396Sbostic 26046483Sbostic /* 26146483Sbostic * Build pointer to the parent directory, allocating memory 26246483Sbostic * as necessary. Max length is 3 for "../", the largest 26346483Sbostic * possible component name, plus a trailing NULL. 26446483Sbostic */ 26546483Sbostic if (bup + 3 + MAXNAMLEN + 1 >= eup) { 26654722Sbostic if ((up = realloc(up, upsize *= 2)) == NULL) 26746483Sbostic goto err; 26854722Sbostic bup = up; 26946483Sbostic eup = up + upsize; 27046483Sbostic } 27146483Sbostic *bup++ = '.'; 27246483Sbostic *bup++ = '.'; 27346483Sbostic *bup = '\0'; 27442396Sbostic 27546483Sbostic /* Open and stat parent directory. */ 27646483Sbostic if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 27740556Skarels goto err; 27842396Sbostic 27946483Sbostic /* Add trailing slash for next directory. */ 28046483Sbostic *bup++ = '/'; 28142396Sbostic 28242396Sbostic /* 28346483Sbostic * If it's a mount point, have to stat each element because 28442396Sbostic * the inode number in the directory is for the entry in the 28542396Sbostic * parent directory, not the inode number of the mounted file. 28642396Sbostic */ 28746483Sbostic save_errno = 0; 28842396Sbostic if (s.st_dev == dev) { 28946483Sbostic for (;;) { 29046483Sbostic if (!(dp = readdir(dir))) 29146483Sbostic goto notfound; 29242396Sbostic if (dp->d_fileno == ino) 29346483Sbostic break; 29446483Sbostic } 29546483Sbostic } else 29646483Sbostic for (;;) { 29746483Sbostic if (!(dp = readdir(dir))) 29846483Sbostic goto notfound; 29942396Sbostic if (ISDOT(dp)) 30042396Sbostic continue; 30146483Sbostic bcopy(dp->d_name, bup, dp->d_namlen + 1); 30246483Sbostic 30346483Sbostic /* Save the first error for later. */ 30442396Sbostic if (lstat(up, &s)) { 30546483Sbostic if (!save_errno) 30646483Sbostic save_errno = errno; 30742396Sbostic errno = 0; 30842396Sbostic continue; 30942396Sbostic } 31046483Sbostic if (s.st_dev == dev && s.st_ino == ino) 31142396Sbostic break; 31242396Sbostic } 31342396Sbostic 31446483Sbostic /* 31546483Sbostic * Check for length of the current name, preceding slash, 31646483Sbostic * leading slash. 31746483Sbostic */ 31846483Sbostic if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { 31946483Sbostic size_t len, off; 32042396Sbostic 32146483Sbostic if (!ptsize) { 32246483Sbostic errno = ERANGE; 32340556Skarels goto err; 32440556Skarels } 32546483Sbostic off = bpt - pt; 32646483Sbostic len = ept - bpt; 32754722Sbostic if ((pt = realloc(pt, ptsize *= 2)) == NULL) 32846483Sbostic goto err; 32946483Sbostic bpt = pt + off; 33046483Sbostic ept = pt + ptsize; 33146483Sbostic (void)bcopy(bpt, ept - len, len); 33246483Sbostic bpt = ept - len; 33340556Skarels } 33446483Sbostic if (!first) 33546483Sbostic *--bpt = '/'; 33646483Sbostic bpt -= dp->d_namlen; 33746483Sbostic bcopy(dp->d_name, bpt, dp->d_namlen); 33846483Sbostic (void)closedir(dir); 33946483Sbostic 34046483Sbostic /* Truncate any file name. */ 34146483Sbostic *bup = '\0'; 3429986Ssam } 34346483Sbostic 34446483Sbostic notfound: 34546483Sbostic /* 34646483Sbostic * If readdir set errno, use it, not any saved error; otherwise, 34746483Sbostic * didn't find the current directory in its parent directory, set 34846483Sbostic * errno to ENOENT. 34946483Sbostic */ 35046483Sbostic if (!errno) 35146483Sbostic errno = save_errno ? save_errno : ENOENT; 35246483Sbostic /* FALLTHROUGH */ 35340556Skarels err: 35446483Sbostic if (ptsize) 35546483Sbostic free(pt); 35646483Sbostic free(up); 35754722Sbostic return (NULL); 3589986Ssam } 359