1/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005,2006,2007 Free Software 2 Foundation, Inc. 3 This file is part of the GNU C Library. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2, or (at your option) 8 any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License along 16 with this program; if not, write to the Free Software Foundation, 17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18 19#if !_LIBC 20# include <config.h> 21# include <unistd.h> 22# include "dirfd.h" 23#endif 24 25#include <errno.h> 26#include <sys/types.h> 27#include <sys/stat.h> 28#include <stdbool.h> 29#include <stddef.h> 30 31#include <fcntl.h> /* For AT_FDCWD on Solaris 9. */ 32 33#ifndef __set_errno 34# define __set_errno(val) (errno = (val)) 35#endif 36 37#include <dirent.h> 38#ifndef _D_EXACT_NAMLEN 39# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name) 40#endif 41#ifndef _D_ALLOC_NAMLEN 42# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1) 43#endif 44 45#include <unistd.h> 46#include <stdlib.h> 47#include <string.h> 48 49#if _LIBC 50# ifndef mempcpy 51# define mempcpy __mempcpy 52# endif 53#endif 54 55#include <limits.h> 56 57/* Work around a bug in Solaris 9 and 10: AT_FDCWD is positive. Its 58 value exceeds INT_MAX, so its use as an int doesn't conform to the 59 C standard, and GCC and Sun C complain in some cases. */ 60#if 0 < AT_FDCWD && AT_FDCWD == 0xffd19553 61# undef AT_FDCWD 62# define AT_FDCWD (-3041965) 63#endif 64 65#ifdef ENAMETOOLONG 66# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG) 67#else 68# define is_ENAMETOOLONG(x) 0 69#endif 70 71#ifndef MAX 72# define MAX(a, b) ((a) < (b) ? (b) : (a)) 73#endif 74#ifndef MIN 75# define MIN(a, b) ((a) < (b) ? (a) : (b)) 76#endif 77 78#ifndef PATH_MAX 79# ifdef MAXPATHLEN 80# define PATH_MAX MAXPATHLEN 81# else 82# define PATH_MAX 1024 83# endif 84#endif 85 86#if D_INO_IN_DIRENT 87# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino)) 88#else 89# define MATCHING_INO(dp, ino) true 90#endif 91 92#if !_LIBC 93# define __getcwd getcwd 94# define __lstat lstat 95# define __closedir closedir 96# define __opendir opendir 97# define __readdir readdir 98#endif 99 100/* The results of opendir() in this file are not used with dirfd and fchdir, 101 therefore save some unnecessary recursion in fchdir.c. */ 102#undef opendir 103#undef closedir 104 105/* Get the name of the current working directory, and put it in SIZE 106 bytes of BUF. Returns NULL if the directory couldn't be determined or 107 SIZE was too small. If successful, returns BUF. In GNU, if BUF is 108 NULL, an array is allocated with `malloc'; the array is SIZE bytes long, 109 unless SIZE == 0, in which case it is as big as necessary. */ 110 111char * 112__getcwd (char *buf, size_t size) 113{ 114 /* Lengths of big file name components and entire file names, and a 115 deep level of file name nesting. These numbers are not upper 116 bounds; they are merely large values suitable for initial 117 allocations, designed to be large enough for most real-world 118 uses. */ 119 enum 120 { 121 BIG_FILE_NAME_COMPONENT_LENGTH = 255, 122 BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1), 123 DEEP_NESTING = 100 124 }; 125 126#ifdef AT_FDCWD 127 int fd = AT_FDCWD; 128 bool fd_needs_closing = false; 129#else 130 char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1]; 131 char *dotlist = dots; 132 size_t dotsize = sizeof dots; 133 size_t dotlen = 0; 134#endif 135 DIR *dirstream = NULL; 136 dev_t rootdev, thisdev; 137 ino_t rootino, thisino; 138 char *dir; 139 register char *dirp; 140 struct stat st; 141 size_t allocated = size; 142 size_t used; 143 144#if HAVE_PARTLY_WORKING_GETCWD 145 /* The system getcwd works, except it sometimes fails when it 146 shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. If 147 AT_FDCWD is not defined, the algorithm below is O(N**2) and this 148 is much slower than the system getcwd (at least on GNU/Linux). 149 So trust the system getcwd's results unless they look 150 suspicious. 151 152 Use the system getcwd even if we have openat support, since the 153 system getcwd works even when a parent is unreadable, while the 154 openat-based approach does not. */ 155 156# undef getcwd 157 dir = getcwd (buf, size); 158 if (dir || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT)) 159 return dir; 160#endif 161 162 if (size == 0) 163 { 164 if (buf != NULL) 165 { 166 __set_errno (EINVAL); 167 return NULL; 168 } 169 170 allocated = BIG_FILE_NAME_LENGTH + 1; 171 } 172 173 if (buf == NULL) 174 { 175 dir = malloc (allocated); 176 if (dir == NULL) 177 return NULL; 178 } 179 else 180 dir = buf; 181 182 dirp = dir + allocated; 183 *--dirp = '\0'; 184 185 if (__lstat (".", &st) < 0) 186 goto lose; 187 thisdev = st.st_dev; 188 thisino = st.st_ino; 189 190 if (__lstat ("/", &st) < 0) 191 goto lose; 192 rootdev = st.st_dev; 193 rootino = st.st_ino; 194 195 while (!(thisdev == rootdev && thisino == rootino)) 196 { 197 struct dirent *d; 198 dev_t dotdev; 199 ino_t dotino; 200 bool mount_point; 201 int parent_status; 202 size_t dirroom; 203 size_t namlen; 204 bool use_d_ino = true; 205 206 /* Look at the parent directory. */ 207#ifdef AT_FDCWD 208 fd = openat (fd, "..", O_RDONLY); 209 if (fd < 0) 210 goto lose; 211 fd_needs_closing = true; 212 parent_status = fstat (fd, &st); 213#else 214 dotlist[dotlen++] = '.'; 215 dotlist[dotlen++] = '.'; 216 dotlist[dotlen] = '\0'; 217 parent_status = __lstat (dotlist, &st); 218#endif 219 if (parent_status != 0) 220 goto lose; 221 222 if (dirstream && __closedir (dirstream) != 0) 223 { 224 dirstream = NULL; 225 goto lose; 226 } 227 228 /* Figure out if this directory is a mount point. */ 229 dotdev = st.st_dev; 230 dotino = st.st_ino; 231 mount_point = dotdev != thisdev; 232 233 /* Search for the last directory. */ 234#ifdef AT_FDCWD 235 dirstream = fdopendir (fd); 236 if (dirstream == NULL) 237 goto lose; 238 /* Reset fd. It may have been closed by fdopendir. */ 239 fd = dirfd (dirstream); 240 fd_needs_closing = false; 241#else 242 dirstream = __opendir (dotlist); 243 if (dirstream == NULL) 244 goto lose; 245 dotlist[dotlen++] = '/'; 246#endif 247 for (;;) 248 { 249 /* Clear errno to distinguish EOF from error if readdir returns 250 NULL. */ 251 __set_errno (0); 252 d = __readdir (dirstream); 253 254 /* When we've iterated through all directory entries without finding 255 one with a matching d_ino, rewind the stream and consider each 256 name again, but this time, using lstat. This is necessary in a 257 chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where 258 .., ../.., ../../.., etc. all had the same device number, yet the 259 d_ino values for entries in / did not match those obtained 260 via lstat. */ 261 if (d == NULL && errno == 0 && use_d_ino) 262 { 263 use_d_ino = false; 264 rewinddir (dirstream); 265 d = __readdir (dirstream); 266 } 267 268 if (d == NULL) 269 { 270 if (errno == 0) 271 /* EOF on dirstream, which can mean e.g., that the current 272 directory has been removed. */ 273 __set_errno (ENOENT); 274 goto lose; 275 } 276 if (d->d_name[0] == '.' && 277 (d->d_name[1] == '\0' || 278 (d->d_name[1] == '.' && d->d_name[2] == '\0'))) 279 continue; 280 281 if (use_d_ino) 282 { 283 bool match = (MATCHING_INO (d, thisino) || mount_point); 284 if (! match) 285 continue; 286 } 287 288 { 289 int entry_status; 290#ifdef AT_FDCWD 291 entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW); 292#else 293 /* Compute size needed for this file name, or for the file 294 name ".." in the same directory, whichever is larger. 295 Room for ".." might be needed the next time through 296 the outer loop. */ 297 size_t name_alloc = _D_ALLOC_NAMLEN (d); 298 size_t filesize = dotlen + MAX (sizeof "..", name_alloc); 299 300 if (filesize < dotlen) 301 goto memory_exhausted; 302 303 if (dotsize < filesize) 304 { 305 /* My, what a deep directory tree you have, Grandma. */ 306 size_t newsize = MAX (filesize, dotsize * 2); 307 size_t i; 308 if (newsize < dotsize) 309 goto memory_exhausted; 310 if (dotlist != dots) 311 free (dotlist); 312 dotlist = malloc (newsize); 313 if (dotlist == NULL) 314 goto lose; 315 dotsize = newsize; 316 317 i = 0; 318 do 319 { 320 dotlist[i++] = '.'; 321 dotlist[i++] = '.'; 322 dotlist[i++] = '/'; 323 } 324 while (i < dotlen); 325 } 326 327 memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d)); 328 entry_status = __lstat (dotlist, &st); 329#endif 330 /* We don't fail here if we cannot stat() a directory entry. 331 This can happen when (network) file systems fail. If this 332 entry is in fact the one we are looking for we will find 333 out soon as we reach the end of the directory without 334 having found anything. */ 335 if (entry_status == 0 && S_ISDIR (st.st_mode) 336 && st.st_dev == thisdev && st.st_ino == thisino) 337 break; 338 } 339 } 340 341 dirroom = dirp - dir; 342 namlen = _D_EXACT_NAMLEN (d); 343 344 if (dirroom <= namlen) 345 { 346 if (size != 0) 347 { 348 __set_errno (ERANGE); 349 goto lose; 350 } 351 else 352 { 353 char *tmp; 354 size_t oldsize = allocated; 355 356 allocated += MAX (allocated, namlen); 357 if (allocated < oldsize 358 || ! (tmp = realloc (dir, allocated))) 359 goto memory_exhausted; 360 361 /* Move current contents up to the end of the buffer. 362 This is guaranteed to be non-overlapping. */ 363 dirp = memcpy (tmp + allocated - (oldsize - dirroom), 364 tmp + dirroom, 365 oldsize - dirroom); 366 dir = tmp; 367 } 368 } 369 dirp -= namlen; 370 memcpy (dirp, d->d_name, namlen); 371 *--dirp = '/'; 372 373 thisdev = dotdev; 374 thisino = dotino; 375 } 376 377 if (dirstream && __closedir (dirstream) != 0) 378 { 379 dirstream = NULL; 380 goto lose; 381 } 382 383 if (dirp == &dir[allocated - 1]) 384 *--dirp = '/'; 385 386#ifndef AT_FDCWD 387 if (dotlist != dots) 388 free (dotlist); 389#endif 390 391 used = dir + allocated - dirp; 392 memmove (dir, dirp, used); 393 394 if (size == 0) 395 /* Ensure that the buffer is only as large as necessary. */ 396 buf = realloc (dir, used); 397 398 if (buf == NULL) 399 /* Either buf was NULL all along, or `realloc' failed but 400 we still have the original string. */ 401 buf = dir; 402 403 return buf; 404 405 memory_exhausted: 406 __set_errno (ENOMEM); 407 lose: 408 { 409 int save = errno; 410 if (dirstream) 411 __closedir (dirstream); 412#ifdef AT_FDCWD 413 if (fd_needs_closing) 414 close (fd); 415#else 416 if (dotlist != dots) 417 free (dotlist); 418#endif 419 if (buf == NULL) 420 free (dir); 421 __set_errno (save); 422 } 423 return NULL; 424} 425 426#ifdef weak_alias 427weak_alias (__getcwd, getcwd) 428#endif 429