getcwd.c revision 9272
11573Srgrimes/* 26227Sdg * Copyright (c) 1989, 1991, 1993 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * Redistribution and use in source and binary forms, with or without 61573Srgrimes * modification, are permitted provided that the following conditions 71573Srgrimes * are met: 81573Srgrimes * 1. Redistributions of source code must retain the above copyright 91573Srgrimes * notice, this list of conditions and the following disclaimer. 101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111573Srgrimes * notice, this list of conditions and the following disclaimer in the 121573Srgrimes * documentation and/or other materials provided with the distribution. 131573Srgrimes * 3. All advertising materials mentioning features or use of this software 141573Srgrimes * must display the following acknowledgement: 151573Srgrimes * This product includes software developed by the University of 161573Srgrimes * California, Berkeley and its contributors. 171573Srgrimes * 4. Neither the name of the University nor the names of its contributors 181573Srgrimes * may be used to endorse or promote products derived from this software 191573Srgrimes * without specific prior written permission. 201573Srgrimes * 211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311573Srgrimes * SUCH DAMAGE. 321573Srgrimes */ 331573Srgrimes 341573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 356227Sdgstatic char sccsid[] = "@(#)getcwd.c 8.1 (Berkeley) 6/4/93"; 361573Srgrimes#endif /* LIBC_SCCS and not lint */ 371573Srgrimes 381573Srgrimes#include <sys/param.h> 391573Srgrimes#include <sys/stat.h> 401573Srgrimes 411573Srgrimes#include <errno.h> 421573Srgrimes#include <dirent.h> 431573Srgrimes#include <stdio.h> 441573Srgrimes#include <stdlib.h> 451573Srgrimes#include <string.h> 461573Srgrimes#include <unistd.h> 471573Srgrimes 481573Srgrimes#define ISDOT(dp) \ 491573Srgrimes (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 501573Srgrimes dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 511573Srgrimes 521573Srgrimeschar * 531573Srgrimesgetcwd(pt, size) 541573Srgrimes char *pt; 551573Srgrimes size_t size; 561573Srgrimes{ 571573Srgrimes register struct dirent *dp; 581573Srgrimes register DIR *dir; 591573Srgrimes register dev_t dev; 601573Srgrimes register ino_t ino; 611573Srgrimes register int first; 621573Srgrimes register char *bpt, *bup; 631573Srgrimes struct stat s; 641573Srgrimes dev_t root_dev; 651573Srgrimes ino_t root_ino; 666227Sdg size_t ptsize, upsize; 671573Srgrimes int save_errno; 686227Sdg char *ept, *eup, *up; 691573Srgrimes 701573Srgrimes /* 711573Srgrimes * If no buffer specified by the user, allocate one as necessary. 721573Srgrimes * If a buffer is specified, the size has to be non-zero. The path 731573Srgrimes * is built from the end of the buffer backwards. 741573Srgrimes */ 751573Srgrimes if (pt) { 761573Srgrimes ptsize = 0; 771573Srgrimes if (!size) { 781573Srgrimes errno = EINVAL; 791573Srgrimes return (NULL); 801573Srgrimes } 815072Sbde if (size == 1) { 825072Sbde errno = ERANGE; 835072Sbde return (NULL); 845072Sbde } 851573Srgrimes ept = pt + size; 861573Srgrimes } else { 871573Srgrimes if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 881573Srgrimes return (NULL); 891573Srgrimes ept = pt + ptsize; 901573Srgrimes } 911573Srgrimes bpt = ept - 1; 921573Srgrimes *bpt = '\0'; 931573Srgrimes 941573Srgrimes /* 951573Srgrimes * Allocate bytes (1024 - malloc space) for the string of "../"'s. 961573Srgrimes * Should always be enough (it's 340 levels). If it's not, allocate 976227Sdg * as necessary. Special * case the first stat, it's ".", not "..". 981573Srgrimes */ 991573Srgrimes if ((up = malloc(upsize = 1024 - 4)) == NULL) 1001573Srgrimes goto err; 1011573Srgrimes eup = up + MAXPATHLEN; 1021573Srgrimes bup = up; 1031573Srgrimes up[0] = '.'; 1041573Srgrimes up[1] = '\0'; 1051573Srgrimes 1061573Srgrimes /* Save root values, so know when to stop. */ 1071573Srgrimes if (stat("/", &s)) 1081573Srgrimes goto err; 1091573Srgrimes root_dev = s.st_dev; 1101573Srgrimes root_ino = s.st_ino; 1111573Srgrimes 1121573Srgrimes errno = 0; /* XXX readdir has no error return. */ 1131573Srgrimes 1141573Srgrimes for (first = 1;; first = 0) { 1151573Srgrimes /* Stat the current level. */ 1161573Srgrimes if (lstat(up, &s)) 1171573Srgrimes goto err; 1181573Srgrimes 1191573Srgrimes /* Save current node values. */ 1201573Srgrimes ino = s.st_ino; 1211573Srgrimes dev = s.st_dev; 1221573Srgrimes 1231573Srgrimes /* Check for reaching root. */ 1241573Srgrimes if (root_dev == dev && root_ino == ino) { 1251573Srgrimes *--bpt = '/'; 1261573Srgrimes /* 1271573Srgrimes * It's unclear that it's a requirement to copy the 1281573Srgrimes * path to the beginning of the buffer, but it's always 1291573Srgrimes * been that way and stuff would probably break. 1301573Srgrimes */ 1319272Shsu bcopy(bpt, pt, ept - bpt); 1321573Srgrimes free(up); 1331573Srgrimes return (pt); 1341573Srgrimes } 1351573Srgrimes 1361573Srgrimes /* 1371573Srgrimes * Build pointer to the parent directory, allocating memory 1381573Srgrimes * as necessary. Max length is 3 for "../", the largest 1391573Srgrimes * possible component name, plus a trailing NULL. 1401573Srgrimes */ 1411573Srgrimes if (bup + 3 + MAXNAMLEN + 1 >= eup) { 1421573Srgrimes if ((up = realloc(up, upsize *= 2)) == NULL) 1431573Srgrimes goto err; 1441573Srgrimes bup = up; 1451573Srgrimes eup = up + upsize; 1461573Srgrimes } 1471573Srgrimes *bup++ = '.'; 1481573Srgrimes *bup++ = '.'; 1491573Srgrimes *bup = '\0'; 1501573Srgrimes 1511573Srgrimes /* Open and stat parent directory. */ 1521573Srgrimes if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 1531573Srgrimes goto err; 1541573Srgrimes 1551573Srgrimes /* Add trailing slash for next directory. */ 1561573Srgrimes *bup++ = '/'; 1571573Srgrimes 1581573Srgrimes /* 1591573Srgrimes * If it's a mount point, have to stat each element because 1601573Srgrimes * the inode number in the directory is for the entry in the 1611573Srgrimes * parent directory, not the inode number of the mounted file. 1621573Srgrimes */ 1631573Srgrimes save_errno = 0; 1641573Srgrimes if (s.st_dev == dev) { 1651573Srgrimes for (;;) { 1661573Srgrimes if (!(dp = readdir(dir))) 1671573Srgrimes goto notfound; 1681573Srgrimes if (dp->d_fileno == ino) 1691573Srgrimes break; 1701573Srgrimes } 1711573Srgrimes } else 1721573Srgrimes for (;;) { 1731573Srgrimes if (!(dp = readdir(dir))) 1741573Srgrimes goto notfound; 1751573Srgrimes if (ISDOT(dp)) 1761573Srgrimes continue; 1771573Srgrimes bcopy(dp->d_name, bup, dp->d_namlen + 1); 1781573Srgrimes 1791573Srgrimes /* Save the first error for later. */ 1801573Srgrimes if (lstat(up, &s)) { 1811573Srgrimes if (!save_errno) 1821573Srgrimes save_errno = errno; 1831573Srgrimes errno = 0; 1841573Srgrimes continue; 1851573Srgrimes } 1861573Srgrimes if (s.st_dev == dev && s.st_ino == ino) 1871573Srgrimes break; 1881573Srgrimes } 1891573Srgrimes 1901573Srgrimes /* 1911573Srgrimes * Check for length of the current name, preceding slash, 1921573Srgrimes * leading slash. 1931573Srgrimes */ 1945072Sbde if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 1951573Srgrimes size_t len, off; 1961573Srgrimes 1971573Srgrimes if (!ptsize) { 1981573Srgrimes errno = ERANGE; 1991573Srgrimes goto err; 2001573Srgrimes } 2011573Srgrimes off = bpt - pt; 2021573Srgrimes len = ept - bpt; 2031573Srgrimes if ((pt = realloc(pt, ptsize *= 2)) == NULL) 2041573Srgrimes goto err; 2051573Srgrimes bpt = pt + off; 2061573Srgrimes ept = pt + ptsize; 2079272Shsu bcopy(bpt, ept - len, len); 2081573Srgrimes bpt = ept - len; 2091573Srgrimes } 2101573Srgrimes if (!first) 2111573Srgrimes *--bpt = '/'; 2121573Srgrimes bpt -= dp->d_namlen; 2131573Srgrimes bcopy(dp->d_name, bpt, dp->d_namlen); 2141573Srgrimes (void)closedir(dir); 2151573Srgrimes 2161573Srgrimes /* Truncate any file name. */ 2171573Srgrimes *bup = '\0'; 2181573Srgrimes } 2191573Srgrimes 2201573Srgrimesnotfound: 2211573Srgrimes /* 2221573Srgrimes * If readdir set errno, use it, not any saved error; otherwise, 2231573Srgrimes * didn't find the current directory in its parent directory, set 2241573Srgrimes * errno to ENOENT. 2251573Srgrimes */ 2261573Srgrimes if (!errno) 2271573Srgrimes errno = save_errno ? save_errno : ENOENT; 2281573Srgrimes /* FALLTHROUGH */ 2291573Srgrimeserr: 2301573Srgrimes if (ptsize) 2311573Srgrimes free(pt); 2321573Srgrimes free(up); 2331573Srgrimes return (NULL); 2341573Srgrimes} 235