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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $FreeBSD$ 34 */ 35 36 #if defined(LIBC_SCCS) && !defined(lint) 37 static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; 38 #endif /* LIBC_SCCS and not lint */ 39 40 #include <sys/param.h> 41 #include <sys/stat.h> 42 43 #include <dirent.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <signal.h> 51 52 #define ISDOT(dp) \ 53 (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 54 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 55 56 static int have__getcwd = 1; /* 0 = no, 1 = perhaps, 2 = yes */ 57 58 char * 59 getcwd(pt, size) 60 char *pt; 61 size_t size; 62 { 63 register struct dirent *dp; 64 register DIR *dir = NULL; 65 register dev_t dev; 66 register ino_t ino; 67 register int first; 68 register char *bpt, *bup; 69 struct stat s; 70 dev_t root_dev; 71 ino_t root_ino; 72 size_t ptsize, upsize; 73 int save_errno; 74 char *ept, *eup, *up, c; 75 76 /* 77 * If no buffer specified by the user, allocate one as necessary. 78 * If a buffer is specified, the size has to be non-zero. The path 79 * is built from the end of the buffer backwards. 80 */ 81 if (pt) { 82 ptsize = 0; 83 if (!size) { 84 errno = EINVAL; 85 return (NULL); 86 } 87 if (size == 1) { 88 errno = ERANGE; 89 return (NULL); 90 } 91 ept = pt + size; 92 } else { 93 if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 94 return (NULL); 95 ept = pt + ptsize; 96 } 97 #if defined(__NETBSD_SYSCALLS) 98 have__getcwd = 0; 99 #else 100 if (have__getcwd) { 101 struct sigaction sa, osa; 102 int sigsys_installed = 0; 103 int ret; 104 105 if (have__getcwd == 1) { /* unsure? */ 106 sigemptyset(&sa.sa_mask); 107 sa.sa_flags = 0; 108 sa.sa_handler = SIG_IGN; 109 if (sigaction(SIGSYS, &sa, &osa) >= 0) 110 sigsys_installed = 1; 111 } 112 ret = __getcwd(pt, ept - pt); 113 if (sigsys_installed == 1) { 114 int oerrno = errno; 115 sigaction(SIGSYS, &osa, NULL); 116 errno = oerrno; 117 } 118 /* XXX a bogus syscall seems to return EINVAL(!) */ 119 if (ret < 0 && (errno == ENOSYS || errno == EINVAL)) 120 have__getcwd = 0; 121 else if (have__getcwd == 1) 122 have__getcwd = 2; /* yep, remember we have it */ 123 if (ret == 0) { 124 if (*pt != '/') { 125 bpt = pt; 126 ept = pt + strlen(pt) - 1; 127 while (bpt < ept) { 128 c = *bpt; 129 *bpt++ = *ept; 130 *ept-- = c; 131 } 132 } 133 return (pt); 134 } 135 } 136 #endif 137 bpt = ept - 1; 138 *bpt = '\0'; 139 140 /* 141 * Allocate bytes (1024 - malloc space) for the string of "../"'s. 142 * Should always be enough (it's 340 levels). If it's not, allocate 143 * as necessary. Special case the first stat, it's ".", not "..". 144 */ 145 if ((up = malloc(upsize = 1024 - 4)) == NULL) 146 goto err; 147 eup = up + MAXPATHLEN; 148 bup = up; 149 up[0] = '.'; 150 up[1] = '\0'; 151 152 /* Save root values, so know when to stop. */ 153 if (stat("/", &s)) 154 goto err; 155 root_dev = s.st_dev; 156 root_ino = s.st_ino; 157 158 errno = 0; /* XXX readdir has no error return. */ 159 160 for (first = 1;; first = 0) { 161 /* Stat the current level. */ 162 if (lstat(up, &s)) 163 goto err; 164 165 /* Save current node values. */ 166 ino = s.st_ino; 167 dev = s.st_dev; 168 169 /* Check for reaching root. */ 170 if (root_dev == dev && root_ino == ino) { 171 *--bpt = '/'; 172 /* 173 * It's unclear that it's a requirement to copy the 174 * path to the beginning of the buffer, but it's always 175 * been that way and stuff would probably break. 176 */ 177 bcopy(bpt, pt, ept - bpt); 178 free(up); 179 return (pt); 180 } 181 182 /* 183 * Build pointer to the parent directory, allocating memory 184 * as necessary. Max length is 3 for "../", the largest 185 * possible component name, plus a trailing NULL. 186 */ 187 if (bup + 3 + MAXNAMLEN + 1 >= eup) { 188 if ((up = reallocf(up, upsize *= 2)) == NULL) 189 goto err; 190 bup = up; 191 eup = up + upsize; 192 } 193 *bup++ = '.'; 194 *bup++ = '.'; 195 *bup = '\0'; 196 197 /* Open and stat parent directory. */ 198 if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 199 goto err; 200 201 /* Add trailing slash for next directory. */ 202 *bup++ = '/'; 203 *bup = '\0'; 204 205 /* 206 * If it's a mount point, have to stat each element because 207 * the inode number in the directory is for the entry in the 208 * parent directory, not the inode number of the mounted file. 209 */ 210 save_errno = 0; 211 if (s.st_dev == dev) { 212 for (;;) { 213 if (!(dp = readdir(dir))) 214 goto notfound; 215 if (dp->d_fileno == ino) 216 break; 217 } 218 } else 219 for (;;) { 220 if (!(dp = readdir(dir))) 221 goto notfound; 222 if (ISDOT(dp)) 223 continue; 224 bcopy(dp->d_name, bup, dp->d_namlen + 1); 225 226 /* Save the first error for later. */ 227 if (lstat(up, &s)) { 228 if (!save_errno) 229 save_errno = errno; 230 errno = 0; 231 continue; 232 } 233 if (s.st_dev == dev && s.st_ino == ino) 234 break; 235 } 236 237 /* 238 * Check for length of the current name, preceding slash, 239 * leading slash. 240 */ 241 if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) { 242 size_t len, off; 243 244 if (!ptsize) { 245 errno = ERANGE; 246 goto err; 247 } 248 off = bpt - pt; 249 len = ept - bpt; 250 if ((pt = realloc(pt, ptsize *= 2)) == NULL) 251 goto err; 252 bpt = pt + off; 253 ept = pt + ptsize; 254 bcopy(bpt, ept - len, len); 255 bpt = ept - len; 256 } 257 if (!first) 258 *--bpt = '/'; 259 bpt -= dp->d_namlen; 260 bcopy(dp->d_name, bpt, dp->d_namlen); 261 (void) closedir(dir); 262 dir = NULL; 263 264 /* Truncate any file name. */ 265 *bup = '\0'; 266 } 267 268 notfound: 269 /* 270 * If readdir set errno, use it, not any saved error; otherwise, 271 * didn't find the current directory in its parent directory, set 272 * errno to ENOENT. 273 */ 274 if (!errno) 275 errno = save_errno ? save_errno : ENOENT; 276 /* FALLTHROUGH */ 277 err: 278 save_errno = errno; 279 280 if (ptsize) 281 free(pt); 282 if (dir) 283 (void) closedir(dir); 284 free(up); 285 286 errno = save_errno; 287 return (NULL); 288 } 289