getcwd.c revision 235647
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 * 4. Neither the name of the University nor the names of its contributors 141573Srgrimes * may be used to endorse or promote products derived from this software 151573Srgrimes * without specific prior written permission. 161573Srgrimes * 171573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271573Srgrimes * SUCH DAMAGE. 281573Srgrimes */ 291573Srgrimes 301573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 3123661Speterstatic char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; 321573Srgrimes#endif /* LIBC_SCCS and not lint */ 3390041Sobrien#include <sys/cdefs.h> 3490041Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/getcwd.c 235647 2012-05-19 12:44:27Z gleb $"); 351573Srgrimes 3671579Sdeischen#include "namespace.h" 371573Srgrimes#include <sys/param.h> 381573Srgrimes#include <sys/stat.h> 391573Srgrimes 4023661Speter#include <dirent.h> 411573Srgrimes#include <errno.h> 4223661Speter#include <fcntl.h> 431573Srgrimes#include <stdio.h> 441573Srgrimes#include <stdlib.h> 451573Srgrimes#include <string.h> 461573Srgrimes#include <unistd.h> 4771579Sdeischen#include "un-namespace.h" 481573Srgrimes 49235647Sgleb#include "gen-private.h" 50235647Sgleb 511573Srgrimes#define ISDOT(dp) \ 521573Srgrimes (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 5317141Sjkh (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 541573Srgrimes 55109039Stjrextern int __getcwd(char *, size_t); 56109039Stjr 571573Srgrimeschar * 581573Srgrimesgetcwd(pt, size) 591573Srgrimes char *pt; 601573Srgrimes size_t size; 611573Srgrimes{ 6290041Sobrien struct dirent *dp; 6390041Sobrien DIR *dir = NULL; 6490041Sobrien dev_t dev; 6590041Sobrien ino_t ino; 6690041Sobrien int first; 67198053Sjilles char *bpt; 681573Srgrimes struct stat s; 691573Srgrimes dev_t root_dev; 701573Srgrimes ino_t root_ino; 71198053Sjilles size_t ptsize; 721573Srgrimes int save_errno; 73198053Sjilles char *ept, c; 74198053Sjilles int fd; 751573Srgrimes 761573Srgrimes /* 771573Srgrimes * If no buffer specified by the user, allocate one as necessary. 781573Srgrimes * If a buffer is specified, the size has to be non-zero. The path 791573Srgrimes * is built from the end of the buffer backwards. 801573Srgrimes */ 811573Srgrimes if (pt) { 821573Srgrimes ptsize = 0; 831573Srgrimes if (!size) { 841573Srgrimes errno = EINVAL; 851573Srgrimes return (NULL); 861573Srgrimes } 875072Sbde if (size == 1) { 885072Sbde errno = ERANGE; 895072Sbde return (NULL); 905072Sbde } 911573Srgrimes ept = pt + size; 921573Srgrimes } else { 93150172Sache if ((pt = malloc(ptsize = PATH_MAX)) == NULL) 941573Srgrimes return (NULL); 951573Srgrimes ept = pt + ptsize; 961573Srgrimes } 9765468Speter if (__getcwd(pt, ept - pt) == 0) { 9865468Speter if (*pt != '/') { 9965468Speter bpt = pt; 10065468Speter ept = pt + strlen(pt) - 1; 10165468Speter while (bpt < ept) { 10265468Speter c = *bpt; 10365468Speter *bpt++ = *ept; 10465468Speter *ept-- = c; 10529476Sphk } 10629392Sphk } 10765468Speter return (pt); 10829392Sphk } 1091573Srgrimes bpt = ept - 1; 1101573Srgrimes *bpt = '\0'; 1111573Srgrimes 1121573Srgrimes /* Save root values, so know when to stop. */ 1131573Srgrimes if (stat("/", &s)) 1141573Srgrimes goto err; 1151573Srgrimes root_dev = s.st_dev; 1161573Srgrimes root_ino = s.st_ino; 1171573Srgrimes 1181573Srgrimes errno = 0; /* XXX readdir has no error return. */ 1191573Srgrimes 1201573Srgrimes for (first = 1;; first = 0) { 1211573Srgrimes /* Stat the current level. */ 122235647Sgleb if (dir != NULL ? _fstat(_dirfd(dir), &s) : lstat(".", &s)) 1231573Srgrimes goto err; 1241573Srgrimes 1251573Srgrimes /* Save current node values. */ 1261573Srgrimes ino = s.st_ino; 1271573Srgrimes dev = s.st_dev; 1281573Srgrimes 1291573Srgrimes /* Check for reaching root. */ 1301573Srgrimes if (root_dev == dev && root_ino == ino) { 1311573Srgrimes *--bpt = '/'; 1321573Srgrimes /* 1331573Srgrimes * It's unclear that it's a requirement to copy the 1341573Srgrimes * path to the beginning of the buffer, but it's always 1351573Srgrimes * been that way and stuff would probably break. 1361573Srgrimes */ 1379272Shsu bcopy(bpt, pt, ept - bpt); 138198053Sjilles if (dir) 139198053Sjilles (void) closedir(dir); 1401573Srgrimes return (pt); 1411573Srgrimes } 1421573Srgrimes 1431573Srgrimes /* Open and stat parent directory. */ 144235647Sgleb fd = _openat(dir != NULL ? _dirfd(dir) : AT_FDCWD, 145198053Sjilles "..", O_RDONLY); 146198053Sjilles if (fd == -1) 1471573Srgrimes goto err; 148198053Sjilles if (dir) 149198053Sjilles (void) closedir(dir); 150235647Sgleb if (!(dir = fdopendir(fd)) || _fstat(_dirfd(dir), &s)) { 151198053Sjilles _close(fd); 152198053Sjilles goto err; 153198053Sjilles } 1541573Srgrimes 1551573Srgrimes /* 1561573Srgrimes * If it's a mount point, have to stat each element because 1571573Srgrimes * the inode number in the directory is for the entry in the 1581573Srgrimes * parent directory, not the inode number of the mounted file. 1591573Srgrimes */ 1601573Srgrimes save_errno = 0; 1611573Srgrimes if (s.st_dev == dev) { 1621573Srgrimes for (;;) { 1631573Srgrimes if (!(dp = readdir(dir))) 1641573Srgrimes goto notfound; 1651573Srgrimes if (dp->d_fileno == ino) 1661573Srgrimes break; 1671573Srgrimes } 1681573Srgrimes } else 1691573Srgrimes for (;;) { 1701573Srgrimes if (!(dp = readdir(dir))) 1711573Srgrimes goto notfound; 1721573Srgrimes if (ISDOT(dp)) 1731573Srgrimes continue; 1741573Srgrimes 1751573Srgrimes /* Save the first error for later. */ 176235647Sgleb if (fstatat(_dirfd(dir), dp->d_name, &s, 177198053Sjilles AT_SYMLINK_NOFOLLOW)) { 1781573Srgrimes if (!save_errno) 1791573Srgrimes save_errno = errno; 1801573Srgrimes errno = 0; 1811573Srgrimes continue; 1821573Srgrimes } 1831573Srgrimes if (s.st_dev == dev && s.st_ino == ino) 1841573Srgrimes break; 1851573Srgrimes } 1861573Srgrimes 1871573Srgrimes /* 1881573Srgrimes * Check for length of the current name, preceding slash, 1891573Srgrimes * leading slash. 1901573Srgrimes */ 191150297Sache while (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 1921573Srgrimes size_t len, off; 1931573Srgrimes 1941573Srgrimes if (!ptsize) { 1951573Srgrimes errno = ERANGE; 1961573Srgrimes goto err; 1971573Srgrimes } 1981573Srgrimes off = bpt - pt; 1991573Srgrimes len = ept - bpt; 200109040Stjr if ((pt = reallocf(pt, ptsize *= 2)) == NULL) 2011573Srgrimes goto err; 2021573Srgrimes bpt = pt + off; 2031573Srgrimes ept = pt + ptsize; 2049272Shsu bcopy(bpt, ept - len, len); 2051573Srgrimes bpt = ept - len; 2061573Srgrimes } 2071573Srgrimes if (!first) 2081573Srgrimes *--bpt = '/'; 2091573Srgrimes bpt -= dp->d_namlen; 2101573Srgrimes bcopy(dp->d_name, bpt, dp->d_namlen); 2111573Srgrimes } 2121573Srgrimes 2131573Srgrimesnotfound: 2141573Srgrimes /* 2151573Srgrimes * If readdir set errno, use it, not any saved error; otherwise, 2161573Srgrimes * didn't find the current directory in its parent directory, set 2171573Srgrimes * errno to ENOENT. 2181573Srgrimes */ 2191573Srgrimes if (!errno) 2201573Srgrimes errno = save_errno ? save_errno : ENOENT; 2211573Srgrimes /* FALLTHROUGH */ 2221573Srgrimeserr: 22332530Smckay save_errno = errno; 22432530Smckay 2251573Srgrimes if (ptsize) 2261573Srgrimes free(pt); 22728235Sdg if (dir) 22828235Sdg (void) closedir(dir); 22932530Smckay 23032530Smckay errno = save_errno; 2311573Srgrimes return (NULL); 2321573Srgrimes} 233