1/* 2 * Copyright (c) 1989, 1991, 1993, 1995 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#if defined(LIBC_SCCS) && !defined(lint) 31static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; 32#endif /* LIBC_SCCS and not lint */ 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: src/lib/libc/gen/getcwd.c,v 1.29 2007/01/09 00:27:53 imp Exp $"); 35 36#include "namespace.h" 37#include <sys/param.h> 38#include <sys/stat.h> 39 40#include <dirent.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <unistd.h> 47#include "un-namespace.h" 48 49#define ISDOT(dp) \ 50 (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 51 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 52 53/* 54 * If __getcwd() ever becomes a syscall, we can remove this workaround. 55 * The problem here is that fcntl() assumes a buffer of size MAXPATHLEN, 56 * if size is less than MAXPATHLEN, we need to use a temporary buffer 57 * and see if it fits. We also have to assume that open() or fcntl() 58 * don't fail with errno=ERANGE. 59 */ 60static inline int 61__getcwd(char *buf, size_t size) 62{ 63 int fd, err, save; 64 struct stat dot, pt; 65 char *b; 66 67 if ((fd = open(".", O_RDONLY)) < 0) 68 return -1; 69 if (fstat(fd, &dot) < 0) { 70 save = errno; 71 close(fd); 72 errno = save; 73 return -1; 74 } 75 /* check that the device and inode are non-zero, otherwise punt */ 76 if (dot.st_dev == 0 || dot.st_ino == 0) { 77 close(fd); 78 errno = EINVAL; 79 return -1; 80 } 81 if (size < MAXPATHLEN) { 82 /* the hard case; allocate a buffer of size MAXPATHLEN to use */ 83 b = (char *)alloca(MAXPATHLEN); 84 if (b == NULL) { 85 close(fd); 86 errno = ENOMEM; /* make sure it isn't ERANGE */ 87 return -1; 88 } 89 } else 90 b = buf; 91 92 err = fcntl(fd, F_GETPATH, b); 93 if (err) { 94 save = errno; 95 close(fd); 96 errno = save; 97 return err; 98 } 99 close(fd); 100 /* 101 * now double-check that the path returned by fcntl() has the same 102 * device and inode number as '.'. 103 */ 104 if (stat(b, &pt) < 0) 105 return -1; 106 /* 107 * Since dot.st_dev and dot.st_ino are non-zero, we don't need to 108 * separately test for pt.st_dev and pt.st_ino being non-zero, because 109 * they have to match 110 */ 111 if (dot.st_dev != pt.st_dev || dot.st_ino != pt.st_ino) { 112 errno = EINVAL; 113 return -1; 114 } 115 /* 116 * For the case where we allocated a buffer, check that it can fit 117 * in the real buffer, and copy it over. 118 */ 119 if (size < MAXPATHLEN) { 120 if (strlen(b) >= size) { 121 errno = ERANGE; 122 return -1; 123 } 124 strcpy(buf, b); 125 } 126 return 0; 127} 128 129__private_extern__ char * 130__private_getcwd(pt, size, usegetpath) 131 char *pt; 132 size_t size; 133 int usegetpath; 134{ 135 struct dirent *dp; 136 DIR *dir = NULL; 137 dev_t dev; 138 ino_t ino; 139 int first; 140 char *bpt, *bup; 141 struct stat s; 142 dev_t root_dev; 143 ino_t root_ino; 144 size_t ptsize, upsize; 145 int save_errno; 146 char *ept, *eup, *up; 147 148 /* 149 * If no buffer specified by the user, allocate one as necessary. 150 * If a buffer is specified, the size has to be non-zero. The path 151 * is built from the end of the buffer backwards. 152 */ 153 if (pt) { 154 ptsize = 0; 155 if (!size) { 156 errno = EINVAL; 157 return (NULL); 158 } 159 if (size == 1) { 160 errno = ERANGE; 161 return (NULL); 162 } 163 ept = pt + size; 164 } else { 165 if ((pt = malloc(ptsize = MAXPATHLEN)) == NULL) 166 return (NULL); 167 ept = pt + ptsize; 168 } 169 if (usegetpath) { 170 if (__getcwd(pt, ept - pt) == 0) { 171 return (pt); 172 } else if (errno == ERANGE) /* failed because buffer too small */ 173 return NULL; 174 } 175 bpt = ept - 1; 176 *bpt = '\0'; 177 178 /* 179 * Allocate MAXPATHLEN bytes for the string of "../"'s. 180 * Should always be enough. If it's not, allocate 181 * as necessary. Special case the first stat, it's ".", not "..". 182 */ 183 if ((up = malloc(upsize = MAXPATHLEN)) == NULL) 184 goto err; 185 eup = up + MAXPATHLEN; 186 bup = up; 187 up[0] = '.'; 188 up[1] = '\0'; 189 190 /* Save root values, so know when to stop. */ 191 if (stat("/", &s)) 192 goto err; 193 root_dev = s.st_dev; 194 root_ino = s.st_ino; 195 196 errno = 0; /* XXX readdir has no error return. */ 197 198 for (first = 1;; first = 0) { 199 /* Stat the current level. */ 200 if (lstat(up, &s)) 201 goto err; 202 203 /* Save current node values. */ 204 ino = s.st_ino; 205 dev = s.st_dev; 206 207 /* Check for reaching root. */ 208 if (root_dev == dev && root_ino == ino) { 209 *--bpt = '/'; 210 /* 211 * It's unclear that it's a requirement to copy the 212 * path to the beginning of the buffer, but it's always 213 * been that way and stuff would probably break. 214 */ 215 bcopy(bpt, pt, ept - bpt); 216 free(up); 217 return (pt); 218 } 219 220 /* 221 * Build pointer to the parent directory, allocating memory 222 * as necessary. Max length is 3 for "../", the largest 223 * possible component name, plus a trailing NUL. 224 */ 225 while (bup + 3 + MAXNAMLEN + 1 >= eup) { 226 if ((up = reallocf(up, upsize *= 2)) == NULL) 227 goto err; 228 bup = up; 229 eup = up + upsize; 230 } 231 *bup++ = '.'; 232 *bup++ = '.'; 233 *bup = '\0'; 234 235 /* Open and stat parent directory. */ 236 if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s)) 237 goto err; 238 239 /* Add trailing slash for next directory. */ 240 *bup++ = '/'; 241 *bup = '\0'; 242 243 /* 244 * If it's a mount point, have to stat each element because 245 * the inode number in the directory is for the entry in the 246 * parent directory, not the inode number of the mounted file. 247 */ 248 save_errno = 0; 249 if (s.st_dev == dev) { 250 for (;;) { 251 if (!(dp = readdir(dir))) 252 goto notfound; 253 if (dp->d_fileno == ino) 254 break; 255 } 256 } else 257 for (;;) { 258 if (!(dp = readdir(dir))) 259 goto notfound; 260 if (ISDOT(dp)) 261 continue; 262 bcopy(dp->d_name, bup, dp->d_namlen + 1); 263 264 /* Save the first error for later. */ 265 if (lstat(up, &s)) { 266 if (!save_errno) 267 save_errno = errno; 268 errno = 0; 269 continue; 270 } 271 if (s.st_dev == dev && s.st_ino == ino) 272 break; 273 } 274 275 /* 276 * Check for length of the current name, preceding slash, 277 * leading slash. 278 */ 279 while (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 280 size_t len, off; 281 282 if (!ptsize) { 283 errno = ERANGE; 284 goto err; 285 } 286 off = bpt - pt; 287 len = ept - bpt; 288 if ((pt = reallocf(pt, ptsize *= 2)) == NULL) 289 goto err; 290 bpt = pt + off; 291 ept = pt + ptsize; 292 bcopy(bpt, ept - len, len); 293 bpt = ept - len; 294 } 295 if (!first) 296 *--bpt = '/'; 297 bpt -= dp->d_namlen; 298 bcopy(dp->d_name, bpt, dp->d_namlen); 299 (void) closedir(dir); 300 dir = NULL; 301 302 /* Truncate any file name. */ 303 *bup = '\0'; 304 } 305 306notfound: 307 /* 308 * If readdir set errno, use it, not any saved error; otherwise, 309 * didn't find the current directory in its parent directory, set 310 * errno to ENOENT. 311 */ 312 if (!errno) 313 errno = save_errno ? save_errno : ENOENT; 314 /* FALLTHROUGH */ 315err: 316 save_errno = errno; 317 318 if (ptsize) 319 free(pt); 320 if (dir) 321 (void) closedir(dir); 322 free(up); 323 324 errno = save_errno; 325 return (NULL); 326} 327 328char * 329getcwd(pt, size) 330 char *pt; 331 size_t size; 332{ 333 return __private_getcwd(pt, size, 1); 334} 335