1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 1989, 1991, 1993 3*0Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 4*0Sstevel@tonic-gate * 5*0Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 6*0Sstevel@tonic-gate * modification, are permitted provided that the following conditions 7*0Sstevel@tonic-gate * are met: 8*0Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 9*0Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 10*0Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 11*0Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 12*0Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15*0Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*0Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*0Sstevel@tonic-gate * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18*0Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*0Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*0Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*0Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*0Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*0Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*0Sstevel@tonic-gate * SUCH DAMAGE. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #include "includes.h" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #if !defined(HAVE_GETCWD) 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #if defined(LIBC_SCCS) && !defined(lint) 32*0Sstevel@tonic-gate static char rcsid[] = "$OpenBSD: getcwd.c,v 1.6 2000/07/19 15:25:13 deraadt Exp $"; 33*0Sstevel@tonic-gate #endif /* LIBC_SCCS and not lint */ 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate #include <sys/param.h> 36*0Sstevel@tonic-gate #include <sys/stat.h> 37*0Sstevel@tonic-gate #include <errno.h> 38*0Sstevel@tonic-gate #include <dirent.h> 39*0Sstevel@tonic-gate #include <sys/dir.h> 40*0Sstevel@tonic-gate #include <stdio.h> 41*0Sstevel@tonic-gate #include <stdlib.h> 42*0Sstevel@tonic-gate #include <string.h> 43*0Sstevel@tonic-gate #include "includes.h" 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate #define ISDOT(dp) \ 46*0Sstevel@tonic-gate (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 47*0Sstevel@tonic-gate (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate char * 50*0Sstevel@tonic-gate getcwd(char *pt,size_t size) 51*0Sstevel@tonic-gate { 52*0Sstevel@tonic-gate register struct dirent *dp; 53*0Sstevel@tonic-gate register DIR *dir = NULL; 54*0Sstevel@tonic-gate register dev_t dev; 55*0Sstevel@tonic-gate register ino_t ino; 56*0Sstevel@tonic-gate register int first; 57*0Sstevel@tonic-gate register char *bpt, *bup; 58*0Sstevel@tonic-gate struct stat s; 59*0Sstevel@tonic-gate dev_t root_dev; 60*0Sstevel@tonic-gate ino_t root_ino; 61*0Sstevel@tonic-gate size_t ptsize, upsize; 62*0Sstevel@tonic-gate int save_errno; 63*0Sstevel@tonic-gate char *ept, *eup, *up; 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gate /* 66*0Sstevel@tonic-gate * If no buffer specified by the user, allocate one as necessary. 67*0Sstevel@tonic-gate * If a buffer is specified, the size has to be non-zero. The path 68*0Sstevel@tonic-gate * is built from the end of the buffer backwards. 69*0Sstevel@tonic-gate */ 70*0Sstevel@tonic-gate if (pt) { 71*0Sstevel@tonic-gate ptsize = 0; 72*0Sstevel@tonic-gate if (!size) { 73*0Sstevel@tonic-gate errno = EINVAL; 74*0Sstevel@tonic-gate return (NULL); 75*0Sstevel@tonic-gate } 76*0Sstevel@tonic-gate ept = pt + size; 77*0Sstevel@tonic-gate } else { 78*0Sstevel@tonic-gate if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 79*0Sstevel@tonic-gate return (NULL); 80*0Sstevel@tonic-gate ept = pt + ptsize; 81*0Sstevel@tonic-gate } 82*0Sstevel@tonic-gate bpt = ept - 1; 83*0Sstevel@tonic-gate *bpt = '\0'; 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gate /* 86*0Sstevel@tonic-gate * Allocate bytes (1024 - malloc space) for the string of "../"'s. 87*0Sstevel@tonic-gate * Should always be enough (it's 340 levels). If it's not, allocate 88*0Sstevel@tonic-gate * as necessary. Special * case the first stat, it's ".", not "..". 89*0Sstevel@tonic-gate */ 90*0Sstevel@tonic-gate if ((up = malloc(upsize = 1024 - 4)) == NULL) 91*0Sstevel@tonic-gate goto err; 92*0Sstevel@tonic-gate eup = up + MAXPATHLEN; 93*0Sstevel@tonic-gate bup = up; 94*0Sstevel@tonic-gate up[0] = '.'; 95*0Sstevel@tonic-gate up[1] = '\0'; 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate /* Save root values, so know when to stop. */ 98*0Sstevel@tonic-gate if (stat("/", &s)) 99*0Sstevel@tonic-gate goto err; 100*0Sstevel@tonic-gate root_dev = s.st_dev; 101*0Sstevel@tonic-gate root_ino = s.st_ino; 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate errno = 0; /* XXX readdir has no error return. */ 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate for (first = 1;; first = 0) { 106*0Sstevel@tonic-gate /* Stat the current level. */ 107*0Sstevel@tonic-gate if (lstat(up, &s)) 108*0Sstevel@tonic-gate goto err; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate /* Save current node values. */ 111*0Sstevel@tonic-gate ino = s.st_ino; 112*0Sstevel@tonic-gate dev = s.st_dev; 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate /* Check for reaching root. */ 115*0Sstevel@tonic-gate if (root_dev == dev && root_ino == ino) { 116*0Sstevel@tonic-gate *--bpt = '/'; 117*0Sstevel@tonic-gate /* 118*0Sstevel@tonic-gate * It's unclear that it's a requirement to copy the 119*0Sstevel@tonic-gate * path to the beginning of the buffer, but it's always 120*0Sstevel@tonic-gate * been that way and stuff would probably break. 121*0Sstevel@tonic-gate */ 122*0Sstevel@tonic-gate memmove(pt, bpt, ept - bpt); 123*0Sstevel@tonic-gate free(up); 124*0Sstevel@tonic-gate return (pt); 125*0Sstevel@tonic-gate } 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate /* 128*0Sstevel@tonic-gate * Build pointer to the parent directory, allocating memory 129*0Sstevel@tonic-gate * as necessary. Max length is 3 for "../", the largest 130*0Sstevel@tonic-gate * possible component name, plus a trailing NULL. 131*0Sstevel@tonic-gate */ 132*0Sstevel@tonic-gate if (bup + 3 + MAXNAMLEN + 1 >= eup) { 133*0Sstevel@tonic-gate char *nup; 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate if ((nup = realloc(up, upsize *= 2)) == NULL) 136*0Sstevel@tonic-gate goto err; 137*0Sstevel@tonic-gate up = nup; 138*0Sstevel@tonic-gate bup = up; 139*0Sstevel@tonic-gate eup = up + upsize; 140*0Sstevel@tonic-gate } 141*0Sstevel@tonic-gate *bup++ = '.'; 142*0Sstevel@tonic-gate *bup++ = '.'; 143*0Sstevel@tonic-gate *bup = '\0'; 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate /* Open and stat parent directory. 146*0Sstevel@tonic-gate * RACE?? - replaced fstat(dirfd(dir), &s) w/ lstat(up,&s) 147*0Sstevel@tonic-gate */ 148*0Sstevel@tonic-gate if (!(dir = opendir(up)) || lstat(up,&s)) 149*0Sstevel@tonic-gate goto err; 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate /* Add trailing slash for next directory. */ 152*0Sstevel@tonic-gate *bup++ = '/'; 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate /* 155*0Sstevel@tonic-gate * If it's a mount point, have to stat each element because 156*0Sstevel@tonic-gate * the inode number in the directory is for the entry in the 157*0Sstevel@tonic-gate * parent directory, not the inode number of the mounted file. 158*0Sstevel@tonic-gate */ 159*0Sstevel@tonic-gate save_errno = 0; 160*0Sstevel@tonic-gate if (s.st_dev == dev) { 161*0Sstevel@tonic-gate for (;;) { 162*0Sstevel@tonic-gate if (!(dp = readdir(dir))) 163*0Sstevel@tonic-gate goto notfound; 164*0Sstevel@tonic-gate if (dp->d_fileno == ino) 165*0Sstevel@tonic-gate break; 166*0Sstevel@tonic-gate } 167*0Sstevel@tonic-gate } else 168*0Sstevel@tonic-gate for (;;) { 169*0Sstevel@tonic-gate if (!(dp = readdir(dir))) 170*0Sstevel@tonic-gate goto notfound; 171*0Sstevel@tonic-gate if (ISDOT(dp)) 172*0Sstevel@tonic-gate continue; 173*0Sstevel@tonic-gate memmove(bup, dp->d_name, dp->d_namlen + 1); 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate /* Save the first error for later. */ 176*0Sstevel@tonic-gate if (lstat(up, &s)) { 177*0Sstevel@tonic-gate if (!save_errno) 178*0Sstevel@tonic-gate save_errno = errno; 179*0Sstevel@tonic-gate errno = 0; 180*0Sstevel@tonic-gate continue; 181*0Sstevel@tonic-gate } 182*0Sstevel@tonic-gate if (s.st_dev == dev && s.st_ino == ino) 183*0Sstevel@tonic-gate break; 184*0Sstevel@tonic-gate } 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate /* 187*0Sstevel@tonic-gate * Check for length of the current name, preceding slash, 188*0Sstevel@tonic-gate * leading slash. 189*0Sstevel@tonic-gate */ 190*0Sstevel@tonic-gate if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 191*0Sstevel@tonic-gate size_t len, off; 192*0Sstevel@tonic-gate char *npt; 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate if (!ptsize) { 195*0Sstevel@tonic-gate errno = ERANGE; 196*0Sstevel@tonic-gate goto err; 197*0Sstevel@tonic-gate } 198*0Sstevel@tonic-gate off = bpt - pt; 199*0Sstevel@tonic-gate len = ept - bpt; 200*0Sstevel@tonic-gate if ((npt = realloc(pt, ptsize *= 2)) == NULL) 201*0Sstevel@tonic-gate goto err; 202*0Sstevel@tonic-gate pt = npt; 203*0Sstevel@tonic-gate bpt = pt + off; 204*0Sstevel@tonic-gate ept = pt + ptsize; 205*0Sstevel@tonic-gate memmove(ept - len, bpt, len); 206*0Sstevel@tonic-gate bpt = ept - len; 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate if (!first) 209*0Sstevel@tonic-gate *--bpt = '/'; 210*0Sstevel@tonic-gate bpt -= dp->d_namlen; 211*0Sstevel@tonic-gate memmove(bpt, dp->d_name, dp->d_namlen); 212*0Sstevel@tonic-gate (void)closedir(dir); 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate /* Truncate any file name. */ 215*0Sstevel@tonic-gate *bup = '\0'; 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gate notfound: 219*0Sstevel@tonic-gate /* 220*0Sstevel@tonic-gate * If readdir set errno, use it, not any saved error; otherwise, 221*0Sstevel@tonic-gate * didn't find the current directory in its parent directory, set 222*0Sstevel@tonic-gate * errno to ENOENT. 223*0Sstevel@tonic-gate */ 224*0Sstevel@tonic-gate if (!errno) 225*0Sstevel@tonic-gate errno = save_errno ? save_errno : ENOENT; 226*0Sstevel@tonic-gate /* FALLTHROUGH */ 227*0Sstevel@tonic-gate err: 228*0Sstevel@tonic-gate if (ptsize) 229*0Sstevel@tonic-gate free(pt); 230*0Sstevel@tonic-gate if (up) 231*0Sstevel@tonic-gate free(up); 232*0Sstevel@tonic-gate if (dir) 233*0Sstevel@tonic-gate (void)closedir(dir); 234*0Sstevel@tonic-gate return (NULL); 235*0Sstevel@tonic-gate } 236*0Sstevel@tonic-gate 237*0Sstevel@tonic-gate #endif /* !defined(HAVE_GETCWD) */ 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 240