getcwd.c revision 109039
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 */ 3790041Sobrien#include <sys/cdefs.h> 3890041Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/getcwd.c 109039 2003-01-10 02:54:37Z tjr $"); 391573Srgrimes 4071579Sdeischen#include "namespace.h" 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> 5171579Sdeischen#include "un-namespace.h" 521573Srgrimes 531573Srgrimes#define ISDOT(dp) \ 541573Srgrimes (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 5517141Sjkh (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 561573Srgrimes 57109039Stjrextern int __getcwd(char *, size_t); 58109039Stjr 591573Srgrimeschar * 601573Srgrimesgetcwd(pt, size) 611573Srgrimes char *pt; 621573Srgrimes size_t size; 631573Srgrimes{ 6490041Sobrien struct dirent *dp; 6590041Sobrien DIR *dir = NULL; 6690041Sobrien dev_t dev; 6790041Sobrien ino_t ino; 6890041Sobrien int first; 6990041Sobrien char *bpt, *bup; 701573Srgrimes struct stat s; 711573Srgrimes dev_t root_dev; 721573Srgrimes ino_t root_ino; 736227Sdg size_t ptsize, upsize; 741573Srgrimes int save_errno; 7529462Sphk char *ept, *eup, *up, c; 761573Srgrimes 771573Srgrimes /* 781573Srgrimes * If no buffer specified by the user, allocate one as necessary. 791573Srgrimes * If a buffer is specified, the size has to be non-zero. The path 801573Srgrimes * is built from the end of the buffer backwards. 811573Srgrimes */ 821573Srgrimes if (pt) { 831573Srgrimes ptsize = 0; 841573Srgrimes if (!size) { 851573Srgrimes errno = EINVAL; 861573Srgrimes return (NULL); 871573Srgrimes } 885072Sbde if (size == 1) { 895072Sbde errno = ERANGE; 905072Sbde return (NULL); 915072Sbde } 921573Srgrimes ept = pt + size; 931573Srgrimes } else { 941573Srgrimes if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 951573Srgrimes return (NULL); 961573Srgrimes ept = pt + ptsize; 971573Srgrimes } 9865468Speter#if !defined(__NETBSD_SYSCALLS) 9965468Speter if (__getcwd(pt, ept - pt) == 0) { 10065468Speter if (*pt != '/') { 10165468Speter bpt = pt; 10265468Speter ept = pt + strlen(pt) - 1; 10365468Speter while (bpt < ept) { 10465468Speter c = *bpt; 10565468Speter *bpt++ = *ept; 10665468Speter *ept-- = c; 10729476Sphk } 10829392Sphk } 10965468Speter return (pt); 11029392Sphk } 11136060Sjb#endif 1121573Srgrimes bpt = ept - 1; 1131573Srgrimes *bpt = '\0'; 1141573Srgrimes 1151573Srgrimes /* 1161573Srgrimes * Allocate bytes (1024 - malloc space) for the string of "../"'s. 1171573Srgrimes * Should always be enough (it's 340 levels). If it's not, allocate 11823661Speter * as necessary. Special case the first stat, it's ".", not "..". 1191573Srgrimes */ 1201573Srgrimes if ((up = malloc(upsize = 1024 - 4)) == NULL) 1211573Srgrimes goto err; 1221573Srgrimes eup = up + MAXPATHLEN; 1231573Srgrimes bup = up; 1241573Srgrimes up[0] = '.'; 1251573Srgrimes up[1] = '\0'; 1261573Srgrimes 1271573Srgrimes /* Save root values, so know when to stop. */ 1281573Srgrimes if (stat("/", &s)) 1291573Srgrimes goto err; 1301573Srgrimes root_dev = s.st_dev; 1311573Srgrimes root_ino = s.st_ino; 1321573Srgrimes 1331573Srgrimes errno = 0; /* XXX readdir has no error return. */ 1341573Srgrimes 1351573Srgrimes for (first = 1;; first = 0) { 1361573Srgrimes /* Stat the current level. */ 1371573Srgrimes if (lstat(up, &s)) 1381573Srgrimes goto err; 1391573Srgrimes 1401573Srgrimes /* Save current node values. */ 1411573Srgrimes ino = s.st_ino; 1421573Srgrimes dev = s.st_dev; 1431573Srgrimes 1441573Srgrimes /* Check for reaching root. */ 1451573Srgrimes if (root_dev == dev && root_ino == ino) { 1461573Srgrimes *--bpt = '/'; 1471573Srgrimes /* 1481573Srgrimes * It's unclear that it's a requirement to copy the 1491573Srgrimes * path to the beginning of the buffer, but it's always 1501573Srgrimes * been that way and stuff would probably break. 1511573Srgrimes */ 1529272Shsu bcopy(bpt, pt, ept - bpt); 1531573Srgrimes free(up); 1541573Srgrimes return (pt); 1551573Srgrimes } 1561573Srgrimes 1571573Srgrimes /* 1581573Srgrimes * Build pointer to the parent directory, allocating memory 1591573Srgrimes * as necessary. Max length is 3 for "../", the largest 160102117Sjmallett * possible component name, plus a trailing NUL. 1611573Srgrimes */ 1621573Srgrimes if (bup + 3 + MAXNAMLEN + 1 >= eup) { 16339327Simp if ((up = reallocf(up, upsize *= 2)) == NULL) 1641573Srgrimes goto err; 1651573Srgrimes bup = up; 1661573Srgrimes eup = up + upsize; 1671573Srgrimes } 1681573Srgrimes *bup++ = '.'; 1691573Srgrimes *bup++ = '.'; 1701573Srgrimes *bup = '\0'; 1711573Srgrimes 1721573Srgrimes /* Open and stat parent directory. */ 17371579Sdeischen if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s)) 1741573Srgrimes goto err; 1751573Srgrimes 1761573Srgrimes /* Add trailing slash for next directory. */ 1771573Srgrimes *bup++ = '/'; 17818971Speter *bup = '\0'; 1791573Srgrimes 1801573Srgrimes /* 1811573Srgrimes * If it's a mount point, have to stat each element because 1821573Srgrimes * the inode number in the directory is for the entry in the 1831573Srgrimes * parent directory, not the inode number of the mounted file. 1841573Srgrimes */ 1851573Srgrimes save_errno = 0; 1861573Srgrimes if (s.st_dev == dev) { 1871573Srgrimes for (;;) { 1881573Srgrimes if (!(dp = readdir(dir))) 1891573Srgrimes goto notfound; 1901573Srgrimes if (dp->d_fileno == ino) 1911573Srgrimes break; 1921573Srgrimes } 1931573Srgrimes } else 1941573Srgrimes for (;;) { 1951573Srgrimes if (!(dp = readdir(dir))) 1961573Srgrimes goto notfound; 1971573Srgrimes if (ISDOT(dp)) 1981573Srgrimes continue; 1991573Srgrimes bcopy(dp->d_name, bup, dp->d_namlen + 1); 2001573Srgrimes 2011573Srgrimes /* Save the first error for later. */ 2021573Srgrimes if (lstat(up, &s)) { 2031573Srgrimes if (!save_errno) 2041573Srgrimes save_errno = errno; 2051573Srgrimes errno = 0; 2061573Srgrimes continue; 2071573Srgrimes } 2081573Srgrimes if (s.st_dev == dev && s.st_ino == ino) 2091573Srgrimes break; 2101573Srgrimes } 2111573Srgrimes 2121573Srgrimes /* 2131573Srgrimes * Check for length of the current name, preceding slash, 2141573Srgrimes * leading slash. 2151573Srgrimes */ 2165072Sbde if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 2171573Srgrimes size_t len, off; 2181573Srgrimes 2191573Srgrimes if (!ptsize) { 2201573Srgrimes errno = ERANGE; 2211573Srgrimes goto err; 2221573Srgrimes } 2231573Srgrimes off = bpt - pt; 2241573Srgrimes len = ept - bpt; 2251573Srgrimes if ((pt = realloc(pt, ptsize *= 2)) == NULL) 2261573Srgrimes goto err; 2271573Srgrimes bpt = pt + off; 2281573Srgrimes ept = pt + ptsize; 2299272Shsu bcopy(bpt, ept - len, len); 2301573Srgrimes bpt = ept - len; 2311573Srgrimes } 2321573Srgrimes if (!first) 2331573Srgrimes *--bpt = '/'; 2341573Srgrimes bpt -= dp->d_namlen; 2351573Srgrimes bcopy(dp->d_name, bpt, dp->d_namlen); 23628235Sdg (void) closedir(dir); 23728235Sdg dir = NULL; 2381573Srgrimes 2391573Srgrimes /* Truncate any file name. */ 2401573Srgrimes *bup = '\0'; 2411573Srgrimes } 2421573Srgrimes 2431573Srgrimesnotfound: 2441573Srgrimes /* 2451573Srgrimes * If readdir set errno, use it, not any saved error; otherwise, 2461573Srgrimes * didn't find the current directory in its parent directory, set 2471573Srgrimes * errno to ENOENT. 2481573Srgrimes */ 2491573Srgrimes if (!errno) 2501573Srgrimes errno = save_errno ? save_errno : ENOENT; 2511573Srgrimes /* FALLTHROUGH */ 2521573Srgrimeserr: 25332530Smckay save_errno = errno; 25432530Smckay 2551573Srgrimes if (ptsize) 2561573Srgrimes free(pt); 25728235Sdg if (dir) 25828235Sdg (void) closedir(dir); 2591573Srgrimes free(up); 26032530Smckay 26132530Smckay errno = save_errno; 2621573Srgrimes return (NULL); 2631573Srgrimes} 264