121345Sdist /* 246483Sbostic * 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*54722Sbostic static char sccsid[] = "@(#)getcwd.c 5.12 (Berkeley) 07/06/92"; 1037209Sbostic #endif /* LIBC_SCCS and not lint */ 1121345Sdist 1210088Ssam #include <sys/param.h> 1310088Ssam #include <sys/stat.h> 14*54722Sbostic 1546483Sbostic #include <errno.h> 1637209Sbostic #include <dirent.h> 1746483Sbostic #include <stdio.h> 1846483Sbostic #include <stdlib.h> 1942396Sbostic #include <string.h> 2046597Sdonn #include <unistd.h> 219986Ssam 2242396Sbostic #define ISDOT(dp) \ 2342396Sbostic (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 2442396Sbostic dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 2542396Sbostic 269986Ssam char * 2746483Sbostic getcwd(pt, size) 2846483Sbostic char *pt; 2946483Sbostic size_t size; 309986Ssam { 3142396Sbostic register struct dirent *dp; 3237209Sbostic register DIR *dir; 3346483Sbostic register dev_t dev; 3442396Sbostic register ino_t ino; 3537209Sbostic register int first; 3646483Sbostic register char *bpt, *bup; 3742396Sbostic struct stat s; 3846483Sbostic dev_t root_dev; 3937209Sbostic ino_t root_ino; 4046483Sbostic size_t ptsize, upsize; 4146483Sbostic int save_errno; 4246483Sbostic char *ept, *eup, *up; 439986Ssam 4446483Sbostic /* 4546483Sbostic * If no buffer specified by the user, allocate one as necessary. 4646483Sbostic * If a buffer is specified, the size has to be non-zero. The path 4746483Sbostic * is built from the end of the buffer backwards. 4846483Sbostic */ 4946483Sbostic if (pt) { 5046483Sbostic ptsize = 0; 5146483Sbostic if (!size) { 5246483Sbostic errno = EINVAL; 53*54722Sbostic return (NULL); 5446483Sbostic } 5546483Sbostic ept = pt + size; 5646483Sbostic } else { 57*54722Sbostic if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 58*54722Sbostic return (NULL); 5946483Sbostic ept = pt + ptsize; 6046483Sbostic } 6146483Sbostic bpt = ept - 1; 6246645Sbostic *bpt = '\0'; 6346483Sbostic 6446483Sbostic /* 6546483Sbostic * Allocate bytes (1024 - malloc space) for the string of "../"'s. 6646483Sbostic * Should always be enough (it's 340 levels). If it's not, allocate 6746483Sbostic * as necessary. Special * case the first stat, it's ".", not "..". 6846483Sbostic */ 69*54722Sbostic if ((up = malloc(upsize = 1024 - 4)) == NULL) 7037209Sbostic goto err; 7146483Sbostic eup = up + MAXPATHLEN; 7246483Sbostic bup = up; 7346483Sbostic up[0] = '.'; 7446483Sbostic up[1] = '\0'; 7546483Sbostic 7646483Sbostic /* Save root values, so know when to stop. */ 7746483Sbostic if (stat("/", &s)) 7846483Sbostic goto err; 7937209Sbostic root_dev = s.st_dev; 8037209Sbostic root_ino = s.st_ino; 8142396Sbostic 8246483Sbostic errno = 0; /* XXX readdir has no error return. */ 8342396Sbostic 8446483Sbostic for (first = 1;; first = 0) { 8546483Sbostic /* Stat the current level. */ 8646483Sbostic if (lstat(up, &s)) 8742396Sbostic goto err; 8842396Sbostic 8946483Sbostic /* Save current node values. */ 9042396Sbostic ino = s.st_ino; 9142396Sbostic dev = s.st_dev; 9242396Sbostic 9346483Sbostic /* Check for reaching root. */ 9442396Sbostic if (root_dev == dev && root_ino == ino) { 9546483Sbostic *--bpt = '/'; 9646483Sbostic /* 9746483Sbostic * It's unclear that it's a requirement to copy the 9846483Sbostic * path to the beginning of the buffer, but it's always 9946483Sbostic * been that way and stuff would probably break. 10046483Sbostic */ 10146483Sbostic (void)bcopy(bpt, pt, ept - bpt); 10246483Sbostic free(up); 103*54722Sbostic return (pt); 10410155Ssam } 10542396Sbostic 10646483Sbostic /* 10746483Sbostic * Build pointer to the parent directory, allocating memory 10846483Sbostic * as necessary. Max length is 3 for "../", the largest 10946483Sbostic * possible component name, plus a trailing NULL. 11046483Sbostic */ 11146483Sbostic if (bup + 3 + MAXNAMLEN + 1 >= eup) { 112*54722Sbostic if ((up = realloc(up, upsize *= 2)) == NULL) 11346483Sbostic goto err; 114*54722Sbostic bup = up; 11546483Sbostic eup = up + upsize; 11646483Sbostic } 11746483Sbostic *bup++ = '.'; 11846483Sbostic *bup++ = '.'; 11946483Sbostic *bup = '\0'; 12042396Sbostic 12146483Sbostic /* Open and stat parent directory. */ 12246483Sbostic if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 12340556Skarels goto err; 12442396Sbostic 12546483Sbostic /* Add trailing slash for next directory. */ 12646483Sbostic *bup++ = '/'; 12742396Sbostic 12842396Sbostic /* 12946483Sbostic * If it's a mount point, have to stat each element because 13042396Sbostic * the inode number in the directory is for the entry in the 13142396Sbostic * parent directory, not the inode number of the mounted file. 13242396Sbostic */ 13346483Sbostic save_errno = 0; 13442396Sbostic if (s.st_dev == dev) { 13546483Sbostic for (;;) { 13646483Sbostic if (!(dp = readdir(dir))) 13746483Sbostic goto notfound; 13842396Sbostic if (dp->d_fileno == ino) 13946483Sbostic break; 14046483Sbostic } 14146483Sbostic } else 14246483Sbostic for (;;) { 14346483Sbostic if (!(dp = readdir(dir))) 14446483Sbostic goto notfound; 14542396Sbostic if (ISDOT(dp)) 14642396Sbostic continue; 14746483Sbostic bcopy(dp->d_name, bup, dp->d_namlen + 1); 14846483Sbostic 14946483Sbostic /* Save the first error for later. */ 15042396Sbostic if (lstat(up, &s)) { 15146483Sbostic if (!save_errno) 15246483Sbostic save_errno = errno; 15342396Sbostic errno = 0; 15442396Sbostic continue; 15542396Sbostic } 15646483Sbostic if (s.st_dev == dev && s.st_ino == ino) 15742396Sbostic break; 15842396Sbostic } 15942396Sbostic 16046483Sbostic /* 16146483Sbostic * Check for length of the current name, preceding slash, 16246483Sbostic * leading slash. 16346483Sbostic */ 16446483Sbostic if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { 16546483Sbostic size_t len, off; 16642396Sbostic 16746483Sbostic if (!ptsize) { 16846483Sbostic errno = ERANGE; 16940556Skarels goto err; 17040556Skarels } 17146483Sbostic off = bpt - pt; 17246483Sbostic len = ept - bpt; 173*54722Sbostic if ((pt = realloc(pt, ptsize *= 2)) == NULL) 17446483Sbostic goto err; 17546483Sbostic bpt = pt + off; 17646483Sbostic ept = pt + ptsize; 17746483Sbostic (void)bcopy(bpt, ept - len, len); 17846483Sbostic bpt = ept - len; 17940556Skarels } 18046483Sbostic if (!first) 18146483Sbostic *--bpt = '/'; 18246483Sbostic bpt -= dp->d_namlen; 18346483Sbostic bcopy(dp->d_name, bpt, dp->d_namlen); 18446483Sbostic (void)closedir(dir); 18546483Sbostic 18646483Sbostic /* Truncate any file name. */ 18746483Sbostic *bup = '\0'; 1889986Ssam } 18946483Sbostic 19046483Sbostic notfound: 19146483Sbostic /* 19246483Sbostic * If readdir set errno, use it, not any saved error; otherwise, 19346483Sbostic * didn't find the current directory in its parent directory, set 19446483Sbostic * errno to ENOENT. 19546483Sbostic */ 19646483Sbostic if (!errno) 19746483Sbostic errno = save_errno ? save_errno : ENOENT; 19846483Sbostic /* FALLTHROUGH */ 19940556Skarels err: 20046483Sbostic if (ptsize) 20146483Sbostic free(pt); 20246483Sbostic free(up); 203*54722Sbostic return (NULL); 2049986Ssam } 205