getcwd.c revision 71579
155714Skris/* 255714Skris * Copyright (c) 1989, 1991, 1993, 1995 355714Skris * The Regents of the University of California. All rights reserved. 455714Skris * 555714Skris * Redistribution and use in source and binary forms, with or without 655714Skris * modification, are permitted provided that the following conditions 755714Skris * are met: 8296465Sdelphij * 1. Redistributions of source code must retain the above copyright 955714Skris * notice, this list of conditions and the following disclaimer. 1055714Skris * 2. Redistributions in binary form must reproduce the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer in the 1255714Skris * documentation and/or other materials provided with the distribution. 1355714Skris * 3. All advertising materials mentioning features or use of this software 1455714Skris * must display the following acknowledgement: 15296465Sdelphij * This product includes software developed by the University of 1655714Skris * California, Berkeley and its contributors. 1755714Skris * 4. Neither the name of the University nor the names of its contributors 1855714Skris * may be used to endorse or promote products derived from this software 1955714Skris * without specific prior written permission. 2055714Skris * 2155714Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22296465Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2455714Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3155714Skris * SUCH DAMAGE. 3255714Skris * 3355714Skris * $FreeBSD: head/lib/libc/gen/getcwd.c 71579 2001-01-24 13:01:12Z deischen $ 3455714Skris */ 3555714Skris 3655714Skris#if defined(LIBC_SCCS) && !defined(lint) 37296465Sdelphijstatic char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; 3855714Skris#endif /* LIBC_SCCS and not lint */ 3955714Skris 40296465Sdelphij#include "namespace.h" 4155714Skris#include <sys/param.h> 4255714Skris#include <sys/stat.h> 4355714Skris 4455714Skris#include <dirent.h> 4555714Skris#include <errno.h> 4655714Skris#include <fcntl.h> 4755714Skris#include <stdio.h> 4855714Skris#include <stdlib.h> 4955714Skris#include <string.h> 5055714Skris#include <unistd.h> 5155714Skris#include "un-namespace.h" 52296465Sdelphij 5355714Skris#define ISDOT(dp) \ 5455714Skris (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 5555714Skris (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 5655714Skris 5755714Skrischar * 5855714Skrisgetcwd(pt, size) 5955714Skris char *pt; 60296465Sdelphij size_t size; 6155714Skris{ 6255714Skris register struct dirent *dp; 6355714Skris register DIR *dir = NULL; 6455714Skris register dev_t dev; 6555714Skris register ino_t ino; 66296465Sdelphij register int first; 67296465Sdelphij register char *bpt, *bup; 6855714Skris struct stat s; 69296465Sdelphij dev_t root_dev; 70296465Sdelphij ino_t root_ino; 71296465Sdelphij size_t ptsize, upsize; 72296465Sdelphij int save_errno; 7355714Skris char *ept, *eup, *up, c; 7455714Skris 75296465Sdelphij /* 7655714Skris * If no buffer specified by the user, allocate one as necessary. 7755714Skris * If a buffer is specified, the size has to be non-zero. The path 7855714Skris * is built from the end of the buffer backwards. 7955714Skris */ 8055714Skris if (pt) { 8155714Skris ptsize = 0; 8255714Skris if (!size) { 8355714Skris errno = EINVAL; 8455714Skris return (NULL); 8555714Skris } 8655714Skris if (size == 1) { 8755714Skris errno = ERANGE; 8855714Skris return (NULL); 8955714Skris } 9055714Skris ept = pt + size; 9155714Skris } else { 9255714Skris if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 9355714Skris return (NULL); 9455714Skris ept = pt + ptsize; 9555714Skris } 9655714Skris#if !defined(__NETBSD_SYSCALLS) 9755714Skris if (__getcwd(pt, ept - pt) == 0) { 9855714Skris if (*pt != '/') { 9955714Skris bpt = pt; 10055714Skris ept = pt + strlen(pt) - 1; 10155714Skris while (bpt < ept) { 10255714Skris c = *bpt; 10355714Skris *bpt++ = *ept; 10455714Skris *ept-- = c; 10555714Skris } 106296465Sdelphij } 10755714Skris return (pt); 10855714Skris } 10955714Skris#endif 11055714Skris bpt = ept - 1; 11155714Skris *bpt = '\0'; 11255714Skris 11355714Skris /* 11455714Skris * Allocate bytes (1024 - malloc space) for the string of "../"'s. 11555714Skris * Should always be enough (it's 340 levels). If it's not, allocate 11655714Skris * as necessary. Special case the first stat, it's ".", not "..". 11755714Skris */ 11855714Skris if ((up = malloc(upsize = 1024 - 4)) == NULL) 119296465Sdelphij goto err; 120296465Sdelphij eup = up + MAXPATHLEN; 12155714Skris bup = up; 12255714Skris up[0] = '.'; 12355714Skris up[1] = '\0'; 124296465Sdelphij 12555714Skris /* Save root values, so know when to stop. */ 12655714Skris if (stat("/", &s)) 127109998Smarkm goto err; 12855714Skris root_dev = s.st_dev; 12955714Skris root_ino = s.st_ino; 13055714Skris 13155714Skris errno = 0; /* XXX readdir has no error return. */ 13255714Skris 13355714Skris for (first = 1;; first = 0) { 13455714Skris /* Stat the current level. */ 135296465Sdelphij if (lstat(up, &s)) 136296465Sdelphij goto err; 137296465Sdelphij 13855714Skris /* Save current node values. */ 13955714Skris ino = s.st_ino; 140296465Sdelphij dev = s.st_dev; 141296465Sdelphij 14255714Skris /* Check for reaching root. */ 143296465Sdelphij if (root_dev == dev && root_ino == ino) { 14455714Skris *--bpt = '/'; 14555714Skris /* 14655714Skris * It's unclear that it's a requirement to copy the 147296465Sdelphij * path to the beginning of the buffer, but it's always 148296465Sdelphij * been that way and stuff would probably break. 149296465Sdelphij */ 150296465Sdelphij bcopy(bpt, pt, ept - bpt); 151296465Sdelphij free(up); 152296465Sdelphij return (pt); 153296465Sdelphij } 154296465Sdelphij 155296465Sdelphij /* 156296465Sdelphij * Build pointer to the parent directory, allocating memory 15755714Skris * as necessary. Max length is 3 for "../", the largest 15855714Skris * possible component name, plus a trailing NULL. 15955714Skris */ 16055714Skris if (bup + 3 + MAXNAMLEN + 1 >= eup) { 16155714Skris if ((up = reallocf(up, upsize *= 2)) == NULL) 16255714Skris goto err; 16355714Skris bup = up; 16455714Skris eup = up + upsize; 16555714Skris } 16655714Skris *bup++ = '.'; 16755714Skris *bup++ = '.'; 168296465Sdelphij *bup = '\0'; 16955714Skris 170296465Sdelphij /* Open and stat parent directory. */ 171296465Sdelphij if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s)) 17255714Skris goto err; 173296465Sdelphij 17455714Skris /* Add trailing slash for next directory. */ 17555714Skris *bup++ = '/'; 17655714Skris *bup = '\0'; 177296465Sdelphij 178296465Sdelphij /* 179296465Sdelphij * If it's a mount point, have to stat each element because 180296465Sdelphij * the inode number in the directory is for the entry in the 181296465Sdelphij * parent directory, not the inode number of the mounted file. 182296465Sdelphij */ 18355714Skris save_errno = 0; 18455714Skris if (s.st_dev == dev) { 18555714Skris for (;;) { 186296465Sdelphij if (!(dp = readdir(dir))) 18755714Skris goto notfound; 18855714Skris if (dp->d_fileno == ino) 18955714Skris break; 190296465Sdelphij } 191296465Sdelphij } else 192296465Sdelphij for (;;) { 193296465Sdelphij if (!(dp = readdir(dir))) 19455714Skris goto notfound; 19555714Skris if (ISDOT(dp)) 196296465Sdelphij continue; 19755714Skris bcopy(dp->d_name, bup, dp->d_namlen + 1); 198296465Sdelphij 199296465Sdelphij /* Save the first error for later. */ 20055714Skris if (lstat(up, &s)) { 201296465Sdelphij if (!save_errno) 20255714Skris save_errno = errno; 20355714Skris errno = 0; 20455714Skris continue; 205296465Sdelphij } 20655714Skris if (s.st_dev == dev && s.st_ino == ino) 207296465Sdelphij break; 20855714Skris } 20955714Skris 21055714Skris /* 21155714Skris * Check for length of the current name, preceding slash, 21255714Skris * leading slash. 213296465Sdelphij */ 214296465Sdelphij if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 215296465Sdelphij size_t len, off; 216296465Sdelphij 217296465Sdelphij if (!ptsize) { 218296465Sdelphij errno = ERANGE; 21955714Skris goto err; 22055714Skris } 22155714Skris off = bpt - pt; 22255714Skris len = ept - bpt; 223296465Sdelphij if ((pt = realloc(pt, ptsize *= 2)) == NULL) 224296465Sdelphij goto err; 22555714Skris bpt = pt + off; 22655714Skris ept = pt + ptsize; 22755714Skris bcopy(bpt, ept - len, len); 228296465Sdelphij bpt = ept - len; 229296465Sdelphij } 230296465Sdelphij if (!first) 231296465Sdelphij *--bpt = '/'; 232296465Sdelphij bpt -= dp->d_namlen; 233296465Sdelphij bcopy(dp->d_name, bpt, dp->d_namlen); 23455714Skris (void) closedir(dir); 23555714Skris dir = NULL; 23655714Skris 237296465Sdelphij /* Truncate any file name. */ 238296465Sdelphij *bup = '\0'; 239296465Sdelphij } 24055714Skris 241296465Sdelphijnotfound: 242296465Sdelphij /* 24355714Skris * If readdir set errno, use it, not any saved error; otherwise, 244296465Sdelphij * didn't find the current directory in its parent directory, set 245296465Sdelphij * errno to ENOENT. 246296465Sdelphij */ 247296465Sdelphij if (!errno) 248296465Sdelphij errno = save_errno ? save_errno : ENOENT; 24955714Skris /* FALLTHROUGH */ 250296465Sdelphijerr: 251296465Sdelphij save_errno = errno; 25255714Skris 253296465Sdelphij if (ptsize) 254296465Sdelphij free(pt); 255296465Sdelphij if (dir) 25655714Skris (void) closedir(dir); 25755714Skris free(up); 25855714Skris 25955714Skris errno = save_errno; 26055714Skris return (NULL); 261296465Sdelphij} 26255714Skris