1 /* 2 * Copyright (c) 1989, 1991, 1993, 1995 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #if defined(LIBC_SCCS) && !defined(lint) 9 static char sccsid[] = "@(#)getcwd.c 8.3 (Berkeley) 02/02/95"; 10 #endif /* LIBC_SCCS and not lint */ 11 12 #include <sys/param.h> 13 #include <sys/stat.h> 14 15 #include <errno.h> 16 #include <dirent.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <unistd.h> 21 22 #define ISDOT(dp) \ 23 (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 24 dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 25 26 char * 27 getcwd(pt, size) 28 char *pt; 29 size_t size; 30 { 31 register struct dirent *dp; 32 register DIR *dir; 33 register dev_t dev; 34 register ino_t ino; 35 register int first; 36 register char *bpt, *bup; 37 struct stat s; 38 dev_t root_dev; 39 ino_t root_ino; 40 size_t ptsize, pwdlen, upsize; 41 int save_errno; 42 char *ept, *eup, *pwd, *up; 43 44 /* Check $PWD -- if it's right, it's fast. */ 45 if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) { 46 dev = s.st_dev; 47 ino = s.st_ino; 48 if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) { 49 pwdlen = strlen(pwd); 50 if (size != 0) { 51 if (pwdlen + 1 > size) { 52 errno = ERANGE; 53 return (NULL); 54 } 55 } else if ((pt = malloc(pwdlen + 1)) == NULL) 56 return (NULL); 57 memmove(pt, pwd, pwdlen); 58 pwd[pwdlen] = '\0'; 59 return (pt); 60 } 61 } 62 63 /* 64 * If no buffer specified by the user, allocate one as necessary. 65 * If a buffer is specified, the size has to be non-zero. The path 66 * is built from the end of the buffer backwards. 67 */ 68 if (pt) { 69 ptsize = 0; 70 if (!size) { 71 errno = EINVAL; 72 return (NULL); 73 } 74 ept = pt + size; 75 } else { 76 if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 77 return (NULL); 78 ept = pt + ptsize; 79 } 80 bpt = ept - 1; 81 *bpt = '\0'; 82 83 /* 84 * Allocate bytes (1024 - malloc space) for the string of "../"'s. 85 * Should always be enough (it's 340 levels). If it's not, allocate 86 * as necessary. Special case the first stat, it's ".", not "..". 87 */ 88 if ((up = malloc(upsize = 1024 - 4)) == NULL) 89 goto err; 90 eup = up + MAXPATHLEN; 91 bup = up; 92 up[0] = '.'; 93 up[1] = '\0'; 94 95 /* Save root values, so know when to stop. */ 96 if (stat("/", &s)) 97 goto err; 98 root_dev = s.st_dev; 99 root_ino = s.st_ino; 100 101 errno = 0; /* XXX readdir has no error return. */ 102 103 for (first = 1;; first = 0) { 104 /* Stat the current level. */ 105 if (lstat(up, &s)) 106 goto err; 107 108 /* Save current node values. */ 109 ino = s.st_ino; 110 dev = s.st_dev; 111 112 /* Check for reaching root. */ 113 if (root_dev == dev && root_ino == ino) { 114 *--bpt = '/'; 115 /* 116 * It's unclear that it's a requirement to copy the 117 * path to the beginning of the buffer, but it's always 118 * been that way and stuff would probably break. 119 */ 120 (void)bcopy(bpt, pt, ept - bpt); 121 free(up); 122 return (pt); 123 } 124 125 /* 126 * Build pointer to the parent directory, allocating memory 127 * as necessary. Max length is 3 for "../", the largest 128 * possible component name, plus a trailing NULL. 129 */ 130 if (bup + 3 + MAXNAMLEN + 1 >= eup) { 131 if ((up = realloc(up, upsize *= 2)) == NULL) 132 goto err; 133 bup = up; 134 eup = up + upsize; 135 } 136 *bup++ = '.'; 137 *bup++ = '.'; 138 *bup = '\0'; 139 140 /* Open and stat parent directory. */ 141 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 142 goto err; 143 144 /* Add trailing slash for next directory. */ 145 *bup++ = '/'; 146 147 /* 148 * If it's a mount point, have to stat each element because 149 * the inode number in the directory is for the entry in the 150 * parent directory, not the inode number of the mounted file. 151 */ 152 save_errno = 0; 153 if (s.st_dev == dev) { 154 for (;;) { 155 if (!(dp = readdir(dir))) 156 goto notfound; 157 if (dp->d_fileno == ino) 158 break; 159 } 160 } else 161 for (;;) { 162 if (!(dp = readdir(dir))) 163 goto notfound; 164 if (ISDOT(dp)) 165 continue; 166 bcopy(dp->d_name, bup, dp->d_namlen + 1); 167 168 /* Save the first error for later. */ 169 if (lstat(up, &s)) { 170 if (!save_errno) 171 save_errno = errno; 172 errno = 0; 173 continue; 174 } 175 if (s.st_dev == dev && s.st_ino == ino) 176 break; 177 } 178 179 /* 180 * Check for length of the current name, preceding slash, 181 * leading slash. 182 */ 183 if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { 184 size_t len, off; 185 186 if (!ptsize) { 187 errno = ERANGE; 188 goto err; 189 } 190 off = bpt - pt; 191 len = ept - bpt; 192 if ((pt = realloc(pt, ptsize *= 2)) == NULL) 193 goto err; 194 bpt = pt + off; 195 ept = pt + ptsize; 196 (void)bcopy(bpt, ept - len, len); 197 bpt = ept - len; 198 } 199 if (!first) 200 *--bpt = '/'; 201 bpt -= dp->d_namlen; 202 bcopy(dp->d_name, bpt, dp->d_namlen); 203 (void)closedir(dir); 204 205 /* Truncate any file name. */ 206 *bup = '\0'; 207 } 208 209 notfound: 210 /* 211 * If readdir set errno, use it, not any saved error; otherwise, 212 * didn't find the current directory in its parent directory, set 213 * errno to ENOENT. 214 */ 215 if (!errno) 216 errno = save_errno ? save_errno : ENOENT; 217 /* FALLTHROUGH */ 218 err: 219 if (ptsize) 220 free(pt); 221 free(up); 222 return (NULL); 223 } 224