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