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