getcwd.c revision 32530
11573Srgrimes/* 223661Speter * Copyright (c) 1989, 1991, 1993, 1995 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) 3523661Speterstatic char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; 361573Srgrimes#endif /* LIBC_SCCS and not lint */ 371573Srgrimes 381573Srgrimes#include <sys/param.h> 391573Srgrimes#include <sys/stat.h> 401573Srgrimes 4123661Speter#include <dirent.h> 421573Srgrimes#include <errno.h> 4323661Speter#include <fcntl.h> 441573Srgrimes#include <stdio.h> 451573Srgrimes#include <stdlib.h> 461573Srgrimes#include <string.h> 471573Srgrimes#include <unistd.h> 4829487Speter#include <signal.h> 491573Srgrimes 501573Srgrimes#define ISDOT(dp) \ 511573Srgrimes (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 5217141Sjkh (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 531573Srgrimes 5429487Speterstatic int have__getcwd = 1; /* 0 = no, 1 = perhaps, 2 = yes */ 5529487Speter 561573Srgrimeschar * 571573Srgrimesgetcwd(pt, size) 581573Srgrimes char *pt; 591573Srgrimes size_t size; 601573Srgrimes{ 611573Srgrimes register struct dirent *dp; 6228235Sdg register DIR *dir = NULL; 631573Srgrimes register dev_t dev; 641573Srgrimes register ino_t ino; 651573Srgrimes register int first; 661573Srgrimes register char *bpt, *bup; 671573Srgrimes struct stat s; 681573Srgrimes dev_t root_dev; 691573Srgrimes ino_t root_ino; 706227Sdg size_t ptsize, upsize; 711573Srgrimes int save_errno; 7229462Sphk char *ept, *eup, *up, c; 731573Srgrimes 741573Srgrimes /* 751573Srgrimes * If no buffer specified by the user, allocate one as necessary. 761573Srgrimes * If a buffer is specified, the size has to be non-zero. The path 771573Srgrimes * is built from the end of the buffer backwards. 781573Srgrimes */ 791573Srgrimes if (pt) { 801573Srgrimes ptsize = 0; 811573Srgrimes if (!size) { 821573Srgrimes errno = EINVAL; 831573Srgrimes return (NULL); 841573Srgrimes } 855072Sbde if (size == 1) { 865072Sbde errno = ERANGE; 875072Sbde return (NULL); 885072Sbde } 891573Srgrimes ept = pt + size; 901573Srgrimes } else { 911573Srgrimes if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 921573Srgrimes return (NULL); 931573Srgrimes ept = pt + ptsize; 941573Srgrimes } 9529487Speter if (have__getcwd) { 9629487Speter struct sigaction sa, osa; 9729487Speter int sigsys_installed = 0; 9829487Speter int ret; 9929487Speter 10029487Speter if (have__getcwd == 1) { /* unsure? */ 10129487Speter bzero(&sa, sizeof(sa)); 10229487Speter sa.sa_handler = SIG_IGN; 10329487Speter if (sigaction(SIGSYS, &sa, &osa) >= 0) 10429487Speter sigsys_installed = 1; 10529487Speter } 10629487Speter ret = __getcwd(pt, ept - pt); 10729487Speter if (sigsys_installed == 1) { 10829487Speter int oerrno = errno; 10929487Speter sigaction(SIGSYS, &osa, NULL); 11029487Speter errno = oerrno; 11129487Speter } 11229487Speter /* XXX a bogus syscall seems to return EINVAL(!) */ 11329487Speter if (ret < 0 && (errno == ENOSYS || errno == EINVAL)) 11429487Speter have__getcwd = 0; 11529487Speter else if (have__getcwd == 1) 11629487Speter have__getcwd = 2; /* yep, remember we have it */ 11729487Speter if (ret == 0) { 11829487Speter if (*pt != '/') { 11929487Speter bpt = pt; 12029487Speter ept = pt + strlen(pt) - 1; 12129487Speter while (bpt < ept) { 12229487Speter c = *bpt; 12329487Speter *bpt++ = *ept; 12429487Speter *ept-- = c; 12529487Speter } 12629476Sphk } 12729487Speter return (pt); 12829392Sphk } 12929392Sphk } 1301573Srgrimes bpt = ept - 1; 1311573Srgrimes *bpt = '\0'; 1321573Srgrimes 1331573Srgrimes /* 1341573Srgrimes * Allocate bytes (1024 - malloc space) for the string of "../"'s. 1351573Srgrimes * Should always be enough (it's 340 levels). If it's not, allocate 13623661Speter * as necessary. Special case the first stat, it's ".", not "..". 1371573Srgrimes */ 1381573Srgrimes if ((up = malloc(upsize = 1024 - 4)) == NULL) 1391573Srgrimes goto err; 1401573Srgrimes eup = up + MAXPATHLEN; 1411573Srgrimes bup = up; 1421573Srgrimes up[0] = '.'; 1431573Srgrimes up[1] = '\0'; 1441573Srgrimes 1451573Srgrimes /* Save root values, so know when to stop. */ 1461573Srgrimes if (stat("/", &s)) 1471573Srgrimes goto err; 1481573Srgrimes root_dev = s.st_dev; 1491573Srgrimes root_ino = s.st_ino; 1501573Srgrimes 1511573Srgrimes errno = 0; /* XXX readdir has no error return. */ 1521573Srgrimes 1531573Srgrimes for (first = 1;; first = 0) { 1541573Srgrimes /* Stat the current level. */ 1551573Srgrimes if (lstat(up, &s)) 1561573Srgrimes goto err; 1571573Srgrimes 1581573Srgrimes /* Save current node values. */ 1591573Srgrimes ino = s.st_ino; 1601573Srgrimes dev = s.st_dev; 1611573Srgrimes 1621573Srgrimes /* Check for reaching root. */ 1631573Srgrimes if (root_dev == dev && root_ino == ino) { 1641573Srgrimes *--bpt = '/'; 1651573Srgrimes /* 1661573Srgrimes * It's unclear that it's a requirement to copy the 1671573Srgrimes * path to the beginning of the buffer, but it's always 1681573Srgrimes * been that way and stuff would probably break. 1691573Srgrimes */ 1709272Shsu bcopy(bpt, pt, ept - bpt); 1711573Srgrimes free(up); 1721573Srgrimes return (pt); 1731573Srgrimes } 1741573Srgrimes 1751573Srgrimes /* 1761573Srgrimes * Build pointer to the parent directory, allocating memory 1771573Srgrimes * as necessary. Max length is 3 for "../", the largest 1781573Srgrimes * possible component name, plus a trailing NULL. 1791573Srgrimes */ 1801573Srgrimes if (bup + 3 + MAXNAMLEN + 1 >= eup) { 1811573Srgrimes if ((up = realloc(up, upsize *= 2)) == NULL) 1821573Srgrimes goto err; 1831573Srgrimes bup = up; 1841573Srgrimes eup = up + upsize; 1851573Srgrimes } 1861573Srgrimes *bup++ = '.'; 1871573Srgrimes *bup++ = '.'; 1881573Srgrimes *bup = '\0'; 1891573Srgrimes 1901573Srgrimes /* Open and stat parent directory. */ 1911573Srgrimes if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 1921573Srgrimes goto err; 1931573Srgrimes 1941573Srgrimes /* Add trailing slash for next directory. */ 1951573Srgrimes *bup++ = '/'; 19618971Speter *bup = '\0'; 1971573Srgrimes 1981573Srgrimes /* 1991573Srgrimes * If it's a mount point, have to stat each element because 2001573Srgrimes * the inode number in the directory is for the entry in the 2011573Srgrimes * parent directory, not the inode number of the mounted file. 2021573Srgrimes */ 2031573Srgrimes save_errno = 0; 2041573Srgrimes if (s.st_dev == dev) { 2051573Srgrimes for (;;) { 2061573Srgrimes if (!(dp = readdir(dir))) 2071573Srgrimes goto notfound; 2081573Srgrimes if (dp->d_fileno == ino) 2091573Srgrimes break; 2101573Srgrimes } 2111573Srgrimes } else 2121573Srgrimes for (;;) { 2131573Srgrimes if (!(dp = readdir(dir))) 2141573Srgrimes goto notfound; 2151573Srgrimes if (ISDOT(dp)) 2161573Srgrimes continue; 2171573Srgrimes bcopy(dp->d_name, bup, dp->d_namlen + 1); 2181573Srgrimes 2191573Srgrimes /* Save the first error for later. */ 2201573Srgrimes if (lstat(up, &s)) { 2211573Srgrimes if (!save_errno) 2221573Srgrimes save_errno = errno; 2231573Srgrimes errno = 0; 2241573Srgrimes continue; 2251573Srgrimes } 2261573Srgrimes if (s.st_dev == dev && s.st_ino == ino) 2271573Srgrimes break; 2281573Srgrimes } 2291573Srgrimes 2301573Srgrimes /* 2311573Srgrimes * Check for length of the current name, preceding slash, 2321573Srgrimes * leading slash. 2331573Srgrimes */ 2345072Sbde if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 2351573Srgrimes size_t len, off; 2361573Srgrimes 2371573Srgrimes if (!ptsize) { 2381573Srgrimes errno = ERANGE; 2391573Srgrimes goto err; 2401573Srgrimes } 2411573Srgrimes off = bpt - pt; 2421573Srgrimes len = ept - bpt; 2431573Srgrimes if ((pt = realloc(pt, ptsize *= 2)) == NULL) 2441573Srgrimes goto err; 2451573Srgrimes bpt = pt + off; 2461573Srgrimes ept = pt + ptsize; 2479272Shsu bcopy(bpt, ept - len, len); 2481573Srgrimes bpt = ept - len; 2491573Srgrimes } 2501573Srgrimes if (!first) 2511573Srgrimes *--bpt = '/'; 2521573Srgrimes bpt -= dp->d_namlen; 2531573Srgrimes bcopy(dp->d_name, bpt, dp->d_namlen); 25428235Sdg (void) closedir(dir); 25528235Sdg dir = NULL; 2561573Srgrimes 2571573Srgrimes /* Truncate any file name. */ 2581573Srgrimes *bup = '\0'; 2591573Srgrimes } 2601573Srgrimes 2611573Srgrimesnotfound: 2621573Srgrimes /* 2631573Srgrimes * If readdir set errno, use it, not any saved error; otherwise, 2641573Srgrimes * didn't find the current directory in its parent directory, set 2651573Srgrimes * errno to ENOENT. 2661573Srgrimes */ 2671573Srgrimes if (!errno) 2681573Srgrimes errno = save_errno ? save_errno : ENOENT; 2691573Srgrimes /* FALLTHROUGH */ 2701573Srgrimeserr: 27132530Smckay save_errno = errno; 27232530Smckay 2731573Srgrimes if (ptsize) 2741573Srgrimes free(pt); 27528235Sdg if (dir) 27628235Sdg (void) closedir(dir); 2771573Srgrimes free(up); 27832530Smckay 27932530Smckay errno = save_errno; 2801573Srgrimes return (NULL); 2811573Srgrimes} 282