121345Sdist /* 2*46483Sbostic * Copyright (c) 1989, 1991 The Regents of the University of California. 337209Sbostic * All rights reserved. 437209Sbostic * 542625Sbostic * %sccs.include.redist.c% 621345Sdist */ 79986Ssam 826565Sdonn #if defined(LIBC_SCCS) && !defined(lint) 9*46483Sbostic static char sccsid[] = "@(#)getcwd.c 5.9 (Berkeley) 02/20/91"; 1037209Sbostic #endif /* LIBC_SCCS and not lint */ 1121345Sdist 1210088Ssam #include <sys/param.h> 1310088Ssam #include <sys/stat.h> 14*46483Sbostic #include <errno.h> 1537209Sbostic #include <dirent.h> 16*46483Sbostic #include <stdio.h> 17*46483Sbostic #include <stdlib.h> 1842396Sbostic #include <string.h> 199986Ssam 2042396Sbostic #define ISDOT(dp) \ 2142396Sbostic (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 2242396Sbostic dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 2342396Sbostic 249986Ssam char * 25*46483Sbostic getcwd(pt, size) 26*46483Sbostic char *pt; 27*46483Sbostic size_t size; 289986Ssam { 2942396Sbostic register struct dirent *dp; 3037209Sbostic register DIR *dir; 31*46483Sbostic register dev_t dev; 3242396Sbostic register ino_t ino; 3337209Sbostic register int first; 34*46483Sbostic register char *bpt, *bup; 3542396Sbostic struct stat s; 36*46483Sbostic dev_t root_dev; 3737209Sbostic ino_t root_ino; 38*46483Sbostic size_t ptsize, upsize; 39*46483Sbostic int save_errno; 40*46483Sbostic char *ept, *eup, *up; 419986Ssam 42*46483Sbostic /* 43*46483Sbostic * If no buffer specified by the user, allocate one as necessary. 44*46483Sbostic * If a buffer is specified, the size has to be non-zero. The path 45*46483Sbostic * is built from the end of the buffer backwards. 46*46483Sbostic */ 47*46483Sbostic if (pt) { 48*46483Sbostic ptsize = 0; 49*46483Sbostic if (!size) { 50*46483Sbostic errno = EINVAL; 51*46483Sbostic return((char *)NULL); 52*46483Sbostic } 53*46483Sbostic ept = pt + size; 54*46483Sbostic } else { 55*46483Sbostic if (!(pt = (char *)malloc(ptsize = 1024 - 4))) 56*46483Sbostic return((char *)NULL); 57*46483Sbostic ept = pt + ptsize; 58*46483Sbostic } 59*46483Sbostic bpt = ept - 1; 60*46483Sbostic *ept = '\0'; 61*46483Sbostic 62*46483Sbostic /* 63*46483Sbostic * Allocate bytes (1024 - malloc space) for the string of "../"'s. 64*46483Sbostic * Should always be enough (it's 340 levels). If it's not, allocate 65*46483Sbostic * as necessary. Special * case the first stat, it's ".", not "..". 66*46483Sbostic */ 67*46483Sbostic if (!(up = (char *)malloc(upsize = 1024 - 4))) 6837209Sbostic goto err; 69*46483Sbostic eup = up + MAXPATHLEN; 70*46483Sbostic bup = up; 71*46483Sbostic up[0] = '.'; 72*46483Sbostic up[1] = '\0'; 73*46483Sbostic 74*46483Sbostic /* Save root values, so know when to stop. */ 75*46483Sbostic if (stat("/", &s)) 76*46483Sbostic goto err; 7737209Sbostic root_dev = s.st_dev; 7837209Sbostic root_ino = s.st_ino; 7942396Sbostic 80*46483Sbostic errno = 0; /* XXX readdir has no error return. */ 8142396Sbostic 82*46483Sbostic for (first = 1;; first = 0) { 83*46483Sbostic /* Stat the current level. */ 84*46483Sbostic if (lstat(up, &s)) 8542396Sbostic goto err; 8642396Sbostic 87*46483Sbostic /* Save current node values. */ 8842396Sbostic ino = s.st_ino; 8942396Sbostic dev = s.st_dev; 9042396Sbostic 91*46483Sbostic /* Check for reaching root. */ 9242396Sbostic if (root_dev == dev && root_ino == ino) { 93*46483Sbostic *--bpt = '/'; 94*46483Sbostic /* 95*46483Sbostic * It's unclear that it's a requirement to copy the 96*46483Sbostic * path to the beginning of the buffer, but it's always 97*46483Sbostic * been that way and stuff would probably break. 98*46483Sbostic */ 99*46483Sbostic (void)bcopy(bpt, pt, ept - bpt); 100*46483Sbostic free(up); 101*46483Sbostic return(pt); 10210155Ssam } 10342396Sbostic 104*46483Sbostic /* 105*46483Sbostic * Build pointer to the parent directory, allocating memory 106*46483Sbostic * as necessary. Max length is 3 for "../", the largest 107*46483Sbostic * possible component name, plus a trailing NULL. 108*46483Sbostic */ 109*46483Sbostic if (bup + 3 + MAXNAMLEN + 1 >= eup) { 110*46483Sbostic if (!(up = (char *)realloc(up, upsize *= 2))) 111*46483Sbostic goto err; 112*46483Sbostic eup = up + upsize; 113*46483Sbostic } 114*46483Sbostic *bup++ = '.'; 115*46483Sbostic *bup++ = '.'; 116*46483Sbostic *bup = '\0'; 11742396Sbostic 118*46483Sbostic /* Open and stat parent directory. */ 119*46483Sbostic if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 12040556Skarels goto err; 12142396Sbostic 122*46483Sbostic /* Add trailing slash for next directory. */ 123*46483Sbostic *bup++ = '/'; 12442396Sbostic 12542396Sbostic /* 126*46483Sbostic * If it's a mount point, have to stat each element because 12742396Sbostic * the inode number in the directory is for the entry in the 12842396Sbostic * parent directory, not the inode number of the mounted file. 12942396Sbostic */ 130*46483Sbostic save_errno = 0; 13142396Sbostic if (s.st_dev == dev) { 132*46483Sbostic for (;;) { 133*46483Sbostic if (!(dp = readdir(dir))) 134*46483Sbostic goto notfound; 13542396Sbostic if (dp->d_fileno == ino) 136*46483Sbostic break; 137*46483Sbostic } 138*46483Sbostic } else 139*46483Sbostic for (;;) { 140*46483Sbostic if (!(dp = readdir(dir))) 141*46483Sbostic goto notfound; 14242396Sbostic if (ISDOT(dp)) 14342396Sbostic continue; 144*46483Sbostic bcopy(dp->d_name, bup, dp->d_namlen + 1); 145*46483Sbostic 146*46483Sbostic /* Save the first error for later. */ 14742396Sbostic if (lstat(up, &s)) { 148*46483Sbostic if (!save_errno) 149*46483Sbostic save_errno = errno; 15042396Sbostic errno = 0; 15142396Sbostic continue; 15242396Sbostic } 153*46483Sbostic if (s.st_dev == dev && s.st_ino == ino) 15442396Sbostic break; 15542396Sbostic } 15642396Sbostic 157*46483Sbostic /* 158*46483Sbostic * Check for length of the current name, preceding slash, 159*46483Sbostic * leading slash. 160*46483Sbostic */ 161*46483Sbostic if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { 162*46483Sbostic size_t len, off; 16342396Sbostic 164*46483Sbostic if (!ptsize) { 165*46483Sbostic errno = ERANGE; 16640556Skarels goto err; 16740556Skarels } 168*46483Sbostic off = bpt - pt; 169*46483Sbostic len = ept - bpt; 170*46483Sbostic if (!(pt = (char *)realloc(pt, ptsize *= 2))) 171*46483Sbostic goto err; 172*46483Sbostic bpt = pt + off; 173*46483Sbostic ept = pt + ptsize; 174*46483Sbostic (void)bcopy(bpt, ept - len, len); 175*46483Sbostic bpt = ept - len; 17640556Skarels } 177*46483Sbostic if (!first) 178*46483Sbostic *--bpt = '/'; 179*46483Sbostic bpt -= dp->d_namlen; 180*46483Sbostic bcopy(dp->d_name, bpt, dp->d_namlen); 181*46483Sbostic (void)closedir(dir); 182*46483Sbostic 183*46483Sbostic /* Truncate any file name. */ 184*46483Sbostic *bup = '\0'; 1859986Ssam } 186*46483Sbostic 187*46483Sbostic notfound: 188*46483Sbostic /* 189*46483Sbostic * If readdir set errno, use it, not any saved error; otherwise, 190*46483Sbostic * didn't find the current directory in its parent directory, set 191*46483Sbostic * errno to ENOENT. 192*46483Sbostic */ 193*46483Sbostic if (!errno) 194*46483Sbostic errno = save_errno ? save_errno : ENOENT; 195*46483Sbostic /* FALLTHROUGH */ 19640556Skarels err: 197*46483Sbostic if (ptsize) 198*46483Sbostic free(pt); 199*46483Sbostic free(up); 200*46483Sbostic return((char *)NULL); 2019986Ssam } 202