1 /* 2 * Copyright (c) 1989, 1991, 1993, 1995 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Jan-Simon Pendry. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #if defined(LIBC_SCCS) && !defined(lint) 12 static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 02/07/95"; 13 #endif /* LIBC_SCCS and not lint */ 14 15 #include <sys/param.h> 16 #include <sys/stat.h> 17 18 #include <dirent.h> 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 static char *getcwd_physical __P((char *, size_t)); 27 28 #define ISDOT(dp) \ 29 (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 30 dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 31 32 char * 33 getcwd(pt, size) 34 char *pt; 35 size_t size; 36 { 37 char *pwd; 38 size_t pwdlen; 39 dev_t dev; 40 ino_t ino; 41 struct stat s; 42 43 /* Check $PWD -- if it's right, it's fast. */ 44 if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) { 45 dev = s.st_dev; 46 ino = s.st_ino; 47 if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) { 48 pwdlen = strlen(pwd); 49 if (size != 0) { 50 if (pwdlen + 1 > size) { 51 errno = ERANGE; 52 return (NULL); 53 } 54 } else if ((pt = malloc(pwdlen + 1)) == NULL) 55 return (NULL); 56 memmove(pt, pwd, pwdlen); 57 pt[pwdlen] = '\0'; 58 return (pt); 59 } 60 } 61 62 return (getcwd_physical(pt, size)); 63 } 64 65 /* 66 * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); 67 * 68 * Find the real name of path, by removing all ".", ".." and symlink 69 * components. Returns (resolved) on success, or (NULL) on failure, 70 * in which case the path which caused trouble is left in (resolved). 71 */ 72 char * 73 realpath(path, resolved) 74 const char *path; 75 char *resolved; 76 { 77 struct stat sb; 78 int fd, n, rootd, serrno; 79 char *p, *q, wbuf[MAXPATHLEN]; 80 81 /* Save the starting point. */ 82 if ((fd = open(".", O_RDONLY)) < 0) { 83 (void)strcpy(resolved, "."); 84 return (NULL); 85 } 86 87 /* 88 * Find the dirname and basename from the path to be resolved. 89 * Change directory to the dirname component. 90 * lstat the basename part. 91 * if it is a symlink, read in the value and loop. 92 * if it is a directory, then change to that directory. 93 * get the current directory name and append the basename. 94 */ 95 (void)strncpy(resolved, path, MAXPATHLEN - 1); 96 resolved[MAXPATHLEN - 1] = '\0'; 97 loop: 98 q = strrchr(resolved, '/'); 99 if (q != NULL) { 100 p = q + 1; 101 if (q == resolved) 102 q = "/"; 103 else { 104 do { 105 --q; 106 } while (q > resolved && *q == '/'); 107 q[1] = '\0'; 108 q = resolved; 109 } 110 if (chdir(q) < 0) 111 goto err1; 112 } else 113 p = resolved; 114 115 /* Deal with the last component. */ 116 if (lstat(p, &sb) == 0) { 117 if (S_ISLNK(sb.st_mode)) { 118 n = readlink(p, resolved, MAXPATHLEN); 119 if (n < 0) 120 goto err1; 121 resolved[n] = '\0'; 122 goto loop; 123 } 124 if (S_ISDIR(sb.st_mode)) { 125 if (chdir(p) < 0) 126 goto err1; 127 p = ""; 128 } 129 } 130 131 /* 132 * Save the last component name and get the full pathname of 133 * the current directory. 134 */ 135 (void)strcpy(wbuf, p); 136 137 /* 138 * Call the inernal internal version of getcwd which 139 * does a physical search rather than using the $PWD short-cut 140 */ 141 if (getcwd_physical(resolved, MAXPATHLEN) == 0) 142 goto err1; 143 144 /* 145 * Join the two strings together, ensuring that the right thing 146 * happens if the last component is empty, or the dirname is root. 147 */ 148 if (resolved[0] == '/' && resolved[1] == '\0') 149 rootd = 1; 150 else 151 rootd = 0; 152 153 if (*wbuf) { 154 if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) { 155 errno = ENAMETOOLONG; 156 goto err1; 157 } 158 if (rootd == 0) 159 (void)strcat(resolved, "/"); 160 (void)strcat(resolved, wbuf); 161 } 162 163 /* Go back to where we came from. */ 164 if (fchdir(fd) < 0) { 165 serrno = errno; 166 goto err2; 167 } 168 169 /* It's okay if the close fails, what's an fd more or less? */ 170 (void)close(fd); 171 return (resolved); 172 173 err1: serrno = errno; 174 (void)fchdir(fd); 175 err2: (void)close(fd); 176 errno = serrno; 177 return (NULL); 178 } 179 180 static char * 181 getcwd_physical(pt, size) 182 char *pt; 183 size_t size; 184 { 185 register struct dirent *dp; 186 register DIR *dir; 187 register dev_t dev; 188 register ino_t ino; 189 register int first; 190 register char *bpt, *bup; 191 struct stat s; 192 dev_t root_dev; 193 ino_t root_ino; 194 size_t ptsize, upsize; 195 int save_errno; 196 char *ept, *eup, *up; 197 198 /* 199 * If no buffer specified by the user, allocate one as necessary. 200 * If a buffer is specified, the size has to be non-zero. The path 201 * is built from the end of the buffer backwards. 202 */ 203 if (pt) { 204 ptsize = 0; 205 if (!size) { 206 errno = EINVAL; 207 return (NULL); 208 } 209 ept = pt + size; 210 } else { 211 if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 212 return (NULL); 213 ept = pt + ptsize; 214 } 215 bpt = ept - 1; 216 *bpt = '\0'; 217 218 /* 219 * Allocate bytes (1024 - malloc space) for the string of "../"'s. 220 * Should always be enough (it's 340 levels). If it's not, allocate 221 * as necessary. Special case the first stat, it's ".", not "..". 222 */ 223 if ((up = malloc(upsize = 1024 - 4)) == NULL) 224 goto err; 225 eup = up + MAXPATHLEN; 226 bup = up; 227 up[0] = '.'; 228 up[1] = '\0'; 229 230 /* Save root values, so know when to stop. */ 231 if (stat("/", &s)) 232 goto err; 233 root_dev = s.st_dev; 234 root_ino = s.st_ino; 235 236 errno = 0; /* XXX readdir has no error return. */ 237 238 for (first = 1;; first = 0) { 239 /* Stat the current level. */ 240 if (lstat(up, &s)) 241 goto err; 242 243 /* Save current node values. */ 244 ino = s.st_ino; 245 dev = s.st_dev; 246 247 /* Check for reaching root. */ 248 if (root_dev == dev && root_ino == ino) { 249 *--bpt = '/'; 250 /* 251 * It's unclear that it's a requirement to copy the 252 * path to the beginning of the buffer, but it's always 253 * been that way and stuff would probably break. 254 */ 255 (void)bcopy(bpt, pt, ept - bpt); 256 free(up); 257 return (pt); 258 } 259 260 /* 261 * Build pointer to the parent directory, allocating memory 262 * as necessary. Max length is 3 for "../", the largest 263 * possible component name, plus a trailing NULL. 264 */ 265 if (bup + 3 + MAXNAMLEN + 1 >= eup) { 266 if ((up = realloc(up, upsize *= 2)) == NULL) 267 goto err; 268 bup = up; 269 eup = up + upsize; 270 } 271 *bup++ = '.'; 272 *bup++ = '.'; 273 *bup = '\0'; 274 275 /* Open and stat parent directory. */ 276 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 277 goto err; 278 279 /* Add trailing slash for next directory. */ 280 *bup++ = '/'; 281 282 /* 283 * If it's a mount point, have to stat each element because 284 * the inode number in the directory is for the entry in the 285 * parent directory, not the inode number of the mounted file. 286 */ 287 save_errno = 0; 288 if (s.st_dev == dev) { 289 for (;;) { 290 if (!(dp = readdir(dir))) 291 goto notfound; 292 if (dp->d_fileno == ino) 293 break; 294 } 295 } else 296 for (;;) { 297 if (!(dp = readdir(dir))) 298 goto notfound; 299 if (ISDOT(dp)) 300 continue; 301 bcopy(dp->d_name, bup, dp->d_namlen + 1); 302 303 /* Save the first error for later. */ 304 if (lstat(up, &s)) { 305 if (!save_errno) 306 save_errno = errno; 307 errno = 0; 308 continue; 309 } 310 if (s.st_dev == dev && s.st_ino == ino) 311 break; 312 } 313 314 /* 315 * Check for length of the current name, preceding slash, 316 * leading slash. 317 */ 318 if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { 319 size_t len, off; 320 321 if (!ptsize) { 322 errno = ERANGE; 323 goto err; 324 } 325 off = bpt - pt; 326 len = ept - bpt; 327 if ((pt = realloc(pt, ptsize *= 2)) == NULL) 328 goto err; 329 bpt = pt + off; 330 ept = pt + ptsize; 331 (void)bcopy(bpt, ept - len, len); 332 bpt = ept - len; 333 } 334 if (!first) 335 *--bpt = '/'; 336 bpt -= dp->d_namlen; 337 bcopy(dp->d_name, bpt, dp->d_namlen); 338 (void)closedir(dir); 339 340 /* Truncate any file name. */ 341 *bup = '\0'; 342 } 343 344 notfound: 345 /* 346 * If readdir set errno, use it, not any saved error; otherwise, 347 * didn't find the current directory in its parent directory, set 348 * errno to ENOENT. 349 */ 350 if (!errno) 351 errno = save_errno ? save_errno : ENOENT; 352 /* FALLTHROUGH */ 353 err: 354 if (ptsize) 355 free(pt); 356 free(up); 357 return (NULL); 358 } 359