getcwd.c revision 106121
1169689Skan/* 2169689Skan * Copyright (c) 1989, 1991, 1993 3169689Skan * The Regents of the University of California. All rights reserved. 4169689Skan * 5169689Skan * Redistribution and use in source and binary forms, with or without 6169689Skan * modification, are permitted provided that the following conditions 7169689Skan * are met: 8169689Skan * 1. Redistributions of source code must retain the above copyright 9169689Skan * notice, this list of conditions and the following disclaimer. 10169689Skan * 2. Redistributions in binary form must reproduce the above copyright 11169689Skan * notice, this list of conditions and the following disclaimer in the 12169689Skan * documentation and/or other materials provided with the distribution. 13169689Skan * 14169689Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24169689Skan * SUCH DAMAGE. 25169689Skan */ 26169689Skan 27169689Skan#include "includes.h" 28169689Skan 29169689Skan#if !defined(HAVE_GETCWD) 30169689Skan 31169689Skan#if defined(LIBC_SCCS) && !defined(lint) 32169689Skanstatic char rcsid[] = "$OpenBSD: getcwd.c,v 1.6 2000/07/19 15:25:13 deraadt Exp $"; 33169689Skan#endif /* LIBC_SCCS and not lint */ 34169689Skan 35169689Skan#include <sys/param.h> 36169689Skan#include <sys/stat.h> 37169689Skan#include <errno.h> 38169689Skan#include <dirent.h> 39169689Skan#include <sys/dir.h> 40169689Skan#include <stdio.h> 41169689Skan#include <stdlib.h> 42169689Skan#include <string.h> 43169689Skan#include "includes.h" 44169689Skan 45169689Skan#define ISDOT(dp) \ 46169689Skan (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 47169689Skan (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 48169689Skan 49169689Skanchar * 50169689Skangetcwd(char *pt,size_t size) 51169689Skan{ 52169689Skan register struct dirent *dp; 53169689Skan register DIR *dir = NULL; 54169689Skan register dev_t dev; 55169689Skan register ino_t ino; 56169689Skan register int first; 57169689Skan register char *bpt, *bup; 58169689Skan struct stat s; 59169689Skan dev_t root_dev; 60169689Skan ino_t root_ino; 61169689Skan size_t ptsize, upsize; 62169689Skan int save_errno; 63169689Skan char *ept, *eup, *up; 64169689Skan 65169689Skan /* 66169689Skan * If no buffer specified by the user, allocate one as necessary. 67169689Skan * If a buffer is specified, the size has to be non-zero. The path 68169689Skan * is built from the end of the buffer backwards. 69169689Skan */ 70169689Skan if (pt) { 71169689Skan ptsize = 0; 72169689Skan if (!size) { 73169689Skan errno = EINVAL; 74169689Skan return (NULL); 75169689Skan } 76169689Skan ept = pt + size; 77169689Skan } else { 78169689Skan if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 79169689Skan return (NULL); 80169689Skan ept = pt + ptsize; 81169689Skan } 82169689Skan bpt = ept - 1; 83169689Skan *bpt = '\0'; 84169689Skan 85169689Skan /* 86169689Skan * Allocate bytes (1024 - malloc space) for the string of "../"'s. 87169689Skan * Should always be enough (it's 340 levels). If it's not, allocate 88169689Skan * as necessary. Special * case the first stat, it's ".", not "..". 89169689Skan */ 90169689Skan if ((up = malloc(upsize = 1024 - 4)) == NULL) 91169689Skan goto err; 92169689Skan eup = up + MAXPATHLEN; 93169689Skan bup = up; 94169689Skan up[0] = '.'; 95169689Skan up[1] = '\0'; 96169689Skan 97169689Skan /* Save root values, so know when to stop. */ 98169689Skan if (stat("/", &s)) 99169689Skan goto err; 100169689Skan root_dev = s.st_dev; 101169689Skan root_ino = s.st_ino; 102169689Skan 103169689Skan errno = 0; /* XXX readdir has no error return. */ 104169689Skan 105169689Skan for (first = 1;; first = 0) { 106169689Skan /* Stat the current level. */ 107169689Skan if (lstat(up, &s)) 108169689Skan goto err; 109169689Skan 110169689Skan /* Save current node values. */ 111169689Skan ino = s.st_ino; 112169689Skan dev = s.st_dev; 113169689Skan 114169689Skan /* Check for reaching root. */ 115169689Skan if (root_dev == dev && root_ino == ino) { 116169689Skan *--bpt = '/'; 117169689Skan /* 118169689Skan * It's unclear that it's a requirement to copy the 119169689Skan * path to the beginning of the buffer, but it's always 120169689Skan * been that way and stuff would probably break. 121169689Skan */ 122169689Skan memmove(pt, bpt, ept - bpt); 123169689Skan free(up); 124169689Skan return (pt); 125169689Skan } 126169689Skan 127169689Skan /* 128169689Skan * Build pointer to the parent directory, allocating memory 129169689Skan * as necessary. Max length is 3 for "../", the largest 130169689Skan * possible component name, plus a trailing NULL. 131169689Skan */ 132169689Skan if (bup + 3 + MAXNAMLEN + 1 >= eup) { 133169689Skan char *nup; 134169689Skan 135169689Skan if ((nup = realloc(up, upsize *= 2)) == NULL) 136169689Skan goto err; 137169689Skan up = nup; 138169689Skan bup = up; 139169689Skan eup = up + upsize; 140169689Skan } 141169689Skan *bup++ = '.'; 142169689Skan *bup++ = '.'; 143169689Skan *bup = '\0'; 144169689Skan 145169689Skan /* Open and stat parent directory. 146169689Skan * RACE?? - replaced fstat(dirfd(dir), &s) w/ lstat(up,&s) 147169689Skan */ 148169689Skan if (!(dir = opendir(up)) || lstat(up,&s)) 149169689Skan goto err; 150169689Skan 151169689Skan /* Add trailing slash for next directory. */ 152169689Skan *bup++ = '/'; 153169689Skan 154169689Skan /* 155169689Skan * If it's a mount point, have to stat each element because 156169689Skan * the inode number in the directory is for the entry in the 157169689Skan * parent directory, not the inode number of the mounted file. 158169689Skan */ 159169689Skan save_errno = 0; 160169689Skan if (s.st_dev == dev) { 161169689Skan for (;;) { 162169689Skan if (!(dp = readdir(dir))) 163169689Skan goto notfound; 164169689Skan if (dp->d_fileno == ino) 165169689Skan break; 166169689Skan } 167169689Skan } else 168169689Skan for (;;) { 169169689Skan if (!(dp = readdir(dir))) 170169689Skan goto notfound; 171169689Skan if (ISDOT(dp)) 172169689Skan continue; 173169689Skan memmove(bup, dp->d_name, dp->d_namlen + 1); 174169689Skan 175169689Skan /* Save the first error for later. */ 176169689Skan if (lstat(up, &s)) { 177169689Skan if (!save_errno) 178169689Skan save_errno = errno; 179169689Skan errno = 0; 180169689Skan continue; 181169689Skan } 182169689Skan if (s.st_dev == dev && s.st_ino == ino) 183169689Skan break; 184169689Skan } 185169689Skan 186169689Skan /* 187169689Skan * Check for length of the current name, preceding slash, 188169689Skan * leading slash. 189169689Skan */ 190169689Skan if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 191169689Skan size_t len, off; 192169689Skan char *npt; 193169689Skan 194169689Skan if (!ptsize) { 195169689Skan errno = ERANGE; 196169689Skan goto err; 197169689Skan } 198169689Skan off = bpt - pt; 199169689Skan len = ept - bpt; 200169689Skan if ((npt = realloc(pt, ptsize *= 2)) == NULL) 201169689Skan goto err; 202169689Skan pt = npt; 203169689Skan bpt = pt + off; 204169689Skan ept = pt + ptsize; 205169689Skan memmove(ept - len, bpt, len); 206169689Skan bpt = ept - len; 207169689Skan } 208169689Skan if (!first) 209169689Skan *--bpt = '/'; 210169689Skan bpt -= dp->d_namlen; 211169689Skan memmove(bpt, dp->d_name, dp->d_namlen); 212169689Skan (void)closedir(dir); 213169689Skan 214169689Skan /* Truncate any file name. */ 215169689Skan *bup = '\0'; 216169689Skan } 217169689Skan 218169689Skannotfound: 219169689Skan /* 220169689Skan * If readdir set errno, use it, not any saved error; otherwise, 221169689Skan * didn't find the current directory in its parent directory, set 222169689Skan * errno to ENOENT. 223169689Skan */ 224169689Skan if (!errno) 225169689Skan errno = save_errno ? save_errno : ENOENT; 226169689Skan /* FALLTHROUGH */ 227169689Skanerr: 228169689Skan if (ptsize) 229169689Skan free(pt); 230169689Skan if (up) 231169689Skan free(up); 232169689Skan if (dir) 233169689Skan (void)closedir(dir); 234169689Skan return (NULL); 235169689Skan} 236169689Skan 237169689Skan#endif /* !defined(HAVE_GETCWD) */ 238169689Skan