getcwd.c revision 150297
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 150297 2005-09-18 17:50:58Z ache $"); 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 { 94150172Sache if ((pt = malloc(ptsize = PATH_MAX)) == NULL) 951573Srgrimes return (NULL); 961573Srgrimes ept = pt + ptsize; 971573Srgrimes } 9865468Speter if (__getcwd(pt, ept - pt) == 0) { 9965468Speter if (*pt != '/') { 10065468Speter bpt = pt; 10165468Speter ept = pt + strlen(pt) - 1; 10265468Speter while (bpt < ept) { 10365468Speter c = *bpt; 10465468Speter *bpt++ = *ept; 10565468Speter *ept-- = c; 10629476Sphk } 10729392Sphk } 10865468Speter return (pt); 10929392Sphk } 1101573Srgrimes bpt = ept - 1; 1111573Srgrimes *bpt = '\0'; 1121573Srgrimes 1131573Srgrimes /* 114150172Sache * Allocate 1024 bytes for the string of "../"'s. 115150172Sache * Should always be enough. If it's not, allocate 11623661Speter * as necessary. Special case the first stat, it's ".", not "..". 1171573Srgrimes */ 118150172Sache if ((up = malloc(upsize = 1024)) == NULL) 1191573Srgrimes goto err; 120150138Sache eup = up + upsize; 1211573Srgrimes bup = up; 1221573Srgrimes up[0] = '.'; 1231573Srgrimes up[1] = '\0'; 1241573Srgrimes 1251573Srgrimes /* Save root values, so know when to stop. */ 1261573Srgrimes if (stat("/", &s)) 1271573Srgrimes goto err; 1281573Srgrimes root_dev = s.st_dev; 1291573Srgrimes root_ino = s.st_ino; 1301573Srgrimes 1311573Srgrimes errno = 0; /* XXX readdir has no error return. */ 1321573Srgrimes 1331573Srgrimes for (first = 1;; first = 0) { 1341573Srgrimes /* Stat the current level. */ 1351573Srgrimes if (lstat(up, &s)) 1361573Srgrimes goto err; 1371573Srgrimes 1381573Srgrimes /* Save current node values. */ 1391573Srgrimes ino = s.st_ino; 1401573Srgrimes dev = s.st_dev; 1411573Srgrimes 1421573Srgrimes /* Check for reaching root. */ 1431573Srgrimes if (root_dev == dev && root_ino == ino) { 1441573Srgrimes *--bpt = '/'; 1451573Srgrimes /* 1461573Srgrimes * It's unclear that it's a requirement to copy the 1471573Srgrimes * path to the beginning of the buffer, but it's always 1481573Srgrimes * been that way and stuff would probably break. 1491573Srgrimes */ 1509272Shsu bcopy(bpt, pt, ept - bpt); 1511573Srgrimes free(up); 1521573Srgrimes return (pt); 1531573Srgrimes } 1541573Srgrimes 1551573Srgrimes /* 1561573Srgrimes * Build pointer to the parent directory, allocating memory 1571573Srgrimes * as necessary. Max length is 3 for "../", the largest 158102117Sjmallett * possible component name, plus a trailing NUL. 1591573Srgrimes */ 160150297Sache while (bup + 3 + MAXNAMLEN + 1 >= eup) { 16139327Simp if ((up = reallocf(up, upsize *= 2)) == NULL) 1621573Srgrimes goto err; 1631573Srgrimes bup = up; 1641573Srgrimes eup = up + upsize; 1651573Srgrimes } 1661573Srgrimes *bup++ = '.'; 1671573Srgrimes *bup++ = '.'; 1681573Srgrimes *bup = '\0'; 1691573Srgrimes 1701573Srgrimes /* Open and stat parent directory. */ 17171579Sdeischen if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s)) 1721573Srgrimes goto err; 1731573Srgrimes 1741573Srgrimes /* Add trailing slash for next directory. */ 1751573Srgrimes *bup++ = '/'; 17618971Speter *bup = '\0'; 1771573Srgrimes 1781573Srgrimes /* 1791573Srgrimes * If it's a mount point, have to stat each element because 1801573Srgrimes * the inode number in the directory is for the entry in the 1811573Srgrimes * parent directory, not the inode number of the mounted file. 1821573Srgrimes */ 1831573Srgrimes save_errno = 0; 1841573Srgrimes if (s.st_dev == dev) { 1851573Srgrimes for (;;) { 1861573Srgrimes if (!(dp = readdir(dir))) 1871573Srgrimes goto notfound; 1881573Srgrimes if (dp->d_fileno == ino) 1891573Srgrimes break; 1901573Srgrimes } 1911573Srgrimes } else 1921573Srgrimes for (;;) { 1931573Srgrimes if (!(dp = readdir(dir))) 1941573Srgrimes goto notfound; 1951573Srgrimes if (ISDOT(dp)) 1961573Srgrimes continue; 1971573Srgrimes bcopy(dp->d_name, bup, dp->d_namlen + 1); 1981573Srgrimes 1991573Srgrimes /* Save the first error for later. */ 2001573Srgrimes if (lstat(up, &s)) { 2011573Srgrimes if (!save_errno) 2021573Srgrimes save_errno = errno; 2031573Srgrimes errno = 0; 2041573Srgrimes continue; 2051573Srgrimes } 2061573Srgrimes if (s.st_dev == dev && s.st_ino == ino) 2071573Srgrimes break; 2081573Srgrimes } 2091573Srgrimes 2101573Srgrimes /* 2111573Srgrimes * Check for length of the current name, preceding slash, 2121573Srgrimes * leading slash. 2131573Srgrimes */ 214150297Sache while (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 2151573Srgrimes size_t len, off; 2161573Srgrimes 2171573Srgrimes if (!ptsize) { 2181573Srgrimes errno = ERANGE; 2191573Srgrimes goto err; 2201573Srgrimes } 2211573Srgrimes off = bpt - pt; 2221573Srgrimes len = ept - bpt; 223109040Stjr if ((pt = reallocf(pt, ptsize *= 2)) == NULL) 2241573Srgrimes goto err; 2251573Srgrimes bpt = pt + off; 2261573Srgrimes ept = pt + ptsize; 2279272Shsu bcopy(bpt, ept - len, len); 2281573Srgrimes bpt = ept - len; 2291573Srgrimes } 2301573Srgrimes if (!first) 2311573Srgrimes *--bpt = '/'; 2321573Srgrimes bpt -= dp->d_namlen; 2331573Srgrimes bcopy(dp->d_name, bpt, dp->d_namlen); 23428235Sdg (void) closedir(dir); 23528235Sdg dir = NULL; 2361573Srgrimes 2371573Srgrimes /* Truncate any file name. */ 2381573Srgrimes *bup = '\0'; 2391573Srgrimes } 2401573Srgrimes 2411573Srgrimesnotfound: 2421573Srgrimes /* 2431573Srgrimes * If readdir set errno, use it, not any saved error; otherwise, 2441573Srgrimes * didn't find the current directory in its parent directory, set 2451573Srgrimes * errno to ENOENT. 2461573Srgrimes */ 2471573Srgrimes if (!errno) 2481573Srgrimes errno = save_errno ? save_errno : ENOENT; 2491573Srgrimes /* FALLTHROUGH */ 2501573Srgrimeserr: 25132530Smckay save_errno = errno; 25232530Smckay 2531573Srgrimes if (ptsize) 2541573Srgrimes free(pt); 25528235Sdg if (dir) 25628235Sdg (void) closedir(dir); 2571573Srgrimes free(up); 25832530Smckay 25932530Smckay errno = save_errno; 2601573Srgrimes return (NULL); 2611573Srgrimes} 262